summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorkoranza <stflynn@hotmail.com>2018-05-18 16:20:13 -0500
committerGitHub <noreply@github.com>2018-05-18 16:20:13 -0500
commit39988eb1cfae043271376bd21fa301ba2115b59c (patch)
tree661b2410afde62935766ccdf1f1803a1e8e7715f
parentb75008292506d49f2641550457354267e11e1116 (diff)
parent06bdf8392bd3e597dbe3ef12c7a819b60e32d7ac (diff)
downloadNim-39988eb1cfae043271376bd21fa301ba2115b59c.tar.gz
Merge pull request #1 from nim-lang/devel
updating my fork with the head repo
-rw-r--r--changelog.md43
-rw-r--r--compiler/aliases.nim4
-rw-r--r--compiler/ast.nim72
-rw-r--r--compiler/astalgo.nim24
-rw-r--r--compiler/bitsets.nim37
-rw-r--r--compiler/ccgcalls.nim26
-rw-r--r--compiler/ccgexprs.nim247
-rw-r--r--compiler/ccgliterals.nim6
-rw-r--r--compiler/ccgmerge.nim34
-rw-r--r--compiler/ccgstmts.nim71
-rw-r--r--compiler/ccgthreadvars.nim12
-rw-r--r--compiler/ccgtrav.nim20
-rw-r--r--compiler/ccgtypes.nim70
-rw-r--r--compiler/ccgutils.nim6
-rw-r--r--compiler/cgen.nim246
-rw-r--r--compiler/cgendata.nim12
-rw-r--r--compiler/cgmeth.nim45
-rw-r--r--compiler/commands.nim865
-rw-r--r--compiler/condsyms.nim75
-rw-r--r--compiler/configuration.nim360
-rw-r--r--compiler/depends.nim6
-rw-r--r--compiler/destroyer.nim50
-rw-r--r--compiler/dfa.nim18
-rw-r--r--compiler/docgen.nim235
-rw-r--r--compiler/docgen2.nim6
-rw-r--r--compiler/evaltempl.nim37
-rw-r--r--compiler/extccomp.nim387
-rw-r--r--compiler/filter_tmpl.nim61
-rw-r--r--compiler/filters.nim43
-rw-r--r--compiler/gorgeimpl.nim4
-rw-r--r--compiler/guards.nim240
-rw-r--r--compiler/hlo.nim20
-rw-r--r--compiler/idgen.nim12
-rw-r--r--compiler/importer.nim50
-rw-r--r--compiler/jsgen.nim723
-rw-r--r--compiler/jstypes.nim33
-rw-r--r--compiler/lambdalifting.nim198
-rw-r--r--compiler/lexer.nim159
-rw-r--r--compiler/liftlocals.nim6
-rw-r--r--compiler/lookups.nim117
-rw-r--r--compiler/lowerings.nim168
-rw-r--r--compiler/magicsys.nim184
-rw-r--r--compiler/main.nim247
-rw-r--r--compiler/modulegraphs.nim56
-rw-r--r--compiler/modulepaths.nim28
-rw-r--r--compiler/modules.nim173
-rw-r--r--compiler/msgs.nim847
-rw-r--r--compiler/nim.nim97
-rw-r--r--compiler/nimblecmd.nim33
-rw-r--r--compiler/nimconf.nim143
-rw-r--r--compiler/nimfix/nimfix.nim16
-rw-r--r--compiler/nimfix/pretty.nim31
-rw-r--r--compiler/nimfix/prettybase.nim18
-rw-r--r--compiler/nimsets.nim55
-rw-r--r--compiler/nversion.nim3
-rw-r--r--compiler/options.nim426
-rw-r--r--compiler/packagehandling.nim23
-rw-r--r--compiler/parampatterns.nim56
-rw-r--r--compiler/parser.nim95
-rw-r--r--compiler/passaux.nim18
-rw-r--r--compiler/passes.nim44
-rw-r--r--compiler/patterns.nim4
-rw-r--r--compiler/pbraces.nim1790
-rw-r--r--compiler/plugins/itersgen.nim10
-rw-r--r--compiler/plugins/locals/locals.nim2
-rw-r--r--compiler/pragmas.nim507
-rw-r--r--compiler/procfind.nim21
-rw-r--r--compiler/renderer.nim87
-rw-r--r--compiler/reorder.nim39
-rw-r--r--compiler/rod.nim112
-rw-r--r--compiler/rodread.nim176
-rw-r--r--compiler/rodwrite.nim59
-rw-r--r--compiler/ropes.nim6
-rw-r--r--compiler/scriptconfig.nim64
-rw-r--r--compiler/sem.nim141
-rw-r--r--compiler/semasgn.nim56
-rw-r--r--compiler/semcall.nim90
-rw-r--r--compiler/semdata.nim64
-rw-r--r--compiler/semexprs.nim520
-rw-r--r--compiler/semfields.nim27
-rw-r--r--compiler/semfold.nim513
-rw-r--r--compiler/semgnrc.nim82
-rw-r--r--compiler/seminst.nim27
-rw-r--r--compiler/semmacrosanity.nim36
-rw-r--r--compiler/semmagic.nim66
-rw-r--r--compiler/semobjconstr.nim80
-rw-r--r--compiler/semparallel.nim128
-rw-r--r--compiler/sempass2.nim211
-rw-r--r--compiler/semstmts.nim706
-rw-r--r--compiler/semtempl.nim77
-rw-r--r--compiler/semtypes.nim319
-rw-r--r--compiler/semtypinst.nim47
-rw-r--r--compiler/service.nim26
-rw-r--r--compiler/sigmatch.nim130
-rw-r--r--compiler/suggest.nim104
-rw-r--r--compiler/syntaxes.nim79
-rw-r--r--compiler/transf.nim101
-rw-r--r--compiler/types.nim79
-rw-r--r--compiler/typesrenderer.nim7
-rw-r--r--compiler/vm.nim279
-rw-r--r--compiler/vmdef.nim9
-rw-r--r--compiler/vmdeps.nim17
-rw-r--r--compiler/vmgen.nim211
-rw-r--r--compiler/vmmarshal.nim73
-rw-r--r--compiler/vmops.nim16
-rw-r--r--compiler/wordrecg.nim6
-rw-r--r--compiler/writetracking.nim12
-rw-r--r--config/nim.cfg10
-rw-r--r--doc/advopt.txt10
-rw-r--r--doc/basicopt.txt3
-rw-r--r--doc/koch.rst33
-rw-r--r--doc/manual.rst113
-rw-r--r--doc/manual/var_t_return.rst20
-rw-r--r--doc/nimc.rst4
-rw-r--r--doc/tut1.rst26
-rw-r--r--examples/httpserver2.nim4
-rw-r--r--koch.nim3
-rw-r--r--lib/core/macros.nim28
-rw-r--r--lib/deprecated/pure/asyncio.nim12
-rw-r--r--lib/deprecated/pure/ftpclient.nim2
-rw-r--r--lib/deprecated/pure/sockets.nim10
-rw-r--r--lib/impure/db_sqlite.nim5
-rw-r--r--lib/impure/nre.nim9
-rw-r--r--lib/impure/rdstdin.nim2
-rw-r--r--lib/js/dom.nim11
-rw-r--r--lib/packages/docutils/rst.nim8
-rw-r--r--lib/packages/docutils/rstast.nim3
-rw-r--r--lib/packages/docutils/rstgen.nim2
-rw-r--r--lib/posix/epoll.nim2
-rw-r--r--lib/posix/inotify.nim2
-rw-r--r--lib/posix/kqueue.nim8
-rw-r--r--lib/posix/linux.nim2
-rw-r--r--lib/posix/posix.nim8
-rw-r--r--lib/posix/posix_linux_amd64.nim4
-rw-r--r--lib/posix/posix_other.nim6
-rw-r--r--lib/posix/termios.nim2
-rw-r--r--lib/pure/algorithm.nim64
-rw-r--r--lib/pure/asyncfutures.nim4
-rw-r--r--lib/pure/asynchttpserver.nim3
-rw-r--r--lib/pure/asyncnet.nim4
-rw-r--r--lib/pure/base64.nim8
-rw-r--r--lib/pure/cgi.nim22
-rw-r--r--lib/pure/collections/critbits.nim27
-rw-r--r--lib/pure/collections/sets.nim14
-rw-r--r--lib/pure/colors.nim2
-rw-r--r--lib/pure/complex.nim2
-rw-r--r--lib/pure/concurrency/threadpool.nim28
-rw-r--r--lib/pure/cookies.nim10
-rw-r--r--lib/pure/db_common.nim3
-rw-r--r--lib/pure/dynlib.nim6
-rw-r--r--lib/pure/encodings.nim4
-rw-r--r--lib/pure/events.nim3
-rw-r--r--lib/pure/fenv.nim57
-rw-r--r--lib/pure/hashes.nim1
-rw-r--r--lib/pure/htmlparser.nim1
-rw-r--r--lib/pure/httpclient.nim47
-rw-r--r--lib/pure/httpcore.nim8
-rw-r--r--lib/pure/httpserver.nim10
-rw-r--r--lib/pure/includes/asynccommon.nim4
-rw-r--r--lib/pure/includes/oserr.nim44
-rw-r--r--lib/pure/json.nim543
-rw-r--r--lib/pure/lexbase.nim2
-rw-r--r--lib/pure/logging.nim6
-rw-r--r--lib/pure/marshal.nim1
-rw-r--r--lib/pure/matchers.nim18
-rw-r--r--lib/pure/math.nim29
-rw-r--r--lib/pure/memfiles.nim2
-rw-r--r--lib/pure/mersenne.nim2
-rw-r--r--lib/pure/mimetypes.nim2
-rw-r--r--lib/pure/nativesockets.nim13
-rw-r--r--lib/pure/net.nim18
-rw-r--r--lib/pure/nimprof.nim2
-rw-r--r--lib/pure/oids.nim6
-rw-r--r--lib/pure/options.nim16
-rw-r--r--lib/pure/os.nim74
-rw-r--r--lib/pure/ospaths.nim9
-rw-r--r--lib/pure/osproc.nim55
-rw-r--r--lib/pure/parsecfg.nim3
-rw-r--r--lib/pure/parsecsv.nim2
-rw-r--r--lib/pure/parsejson.nim535
-rw-r--r--lib/pure/parseopt.nim50
-rw-r--r--lib/pure/parseopt2.nim4
-rw-r--r--lib/pure/parsesql.nim2
-rw-r--r--lib/pure/parseutils.nim111
-rw-r--r--lib/pure/parsexml.nim3
-rw-r--r--lib/pure/pegs.nim54
-rw-r--r--lib/pure/random.nim2
-rw-r--r--lib/pure/ropes.nim4
-rw-r--r--lib/pure/scgi.nim3
-rw-r--r--lib/pure/selectors.nim8
-rw-r--r--lib/pure/smtp.nim2
-rw-r--r--lib/pure/stats.nim2
-rw-r--r--lib/pure/streams.nim31
-rw-r--r--lib/pure/strformat.nim2
-rw-r--r--lib/pure/strmisc.nim2
-rw-r--r--lib/pure/strscans.nim34
-rw-r--r--lib/pure/strtabs.nim37
-rw-r--r--lib/pure/strutils.nim930
-rw-r--r--lib/pure/subexes.nim3
-rw-r--r--lib/pure/terminal.nim81
-rw-r--r--lib/pure/times.nim228
-rw-r--r--lib/pure/unicode.nim10
-rw-r--r--lib/pure/uri.nim95
-rw-r--r--lib/pure/xmldom.nim35
-rw-r--r--lib/pure/xmlparser.nim2
-rw-r--r--lib/pure/xmltree.nim21
-rw-r--r--lib/system.nim98
-rw-r--r--lib/system/assign.nim22
-rw-r--r--lib/system/atomics.nim4
-rw-r--r--lib/system/deepcopy.nim10
-rw-r--r--lib/system/embedded.nim2
-rw-r--r--lib/system/excpt.nim8
-rw-r--r--lib/system/gc.nim2
-rw-r--r--lib/system/jssys.nim487
-rw-r--r--lib/system/osalloc.nim2
-rw-r--r--lib/system/sysstr.nim84
-rw-r--r--lib/windows/winlean.nim13
-rw-r--r--lib/wrappers/iup.nim2
-rw-r--r--lib/wrappers/mysql.nim2
-rw-r--r--lib/wrappers/odbcsql.nim2
-rw-r--r--lib/wrappers/openssl.nim4
-rw-r--r--lib/wrappers/pcre.nim2
-rw-r--r--lib/wrappers/postgres.nim2
-rw-r--r--lib/wrappers/sqlite3.nim2
-rw-r--r--nimsuggest/nimsuggest.nim166
-rw-r--r--tests/array/tarraycons_ptr_generic.nim51
-rw-r--r--tests/array/tarraycons_ptr_generic2.nim17
-rw-r--r--tests/assign/toverload_asgn1.nim1
-rw-r--r--tests/assign/toverload_asgn2.nim1
-rw-r--r--tests/async/tasyncawait.nim12
-rw-r--r--tests/async/tlambda.nim4
-rw-r--r--tests/collections/tcollections_to_string.nim10
-rw-r--r--tests/collections/thashes.nim4
-rw-r--r--tests/collections/tsets.nim52
-rw-r--r--tests/compiles/trecursive_generic_in_compiles.nim4
-rw-r--r--tests/concepts/tstackconcept.nim2
-rw-r--r--tests/constructors/tinvalid_construction.nim4
-rw-r--r--tests/controlflow/tstatret.nim2
-rw-r--r--tests/converter/tconvert.nim2
-rw-r--r--tests/cpp/tcovariancerules.nim16
-rw-r--r--tests/distinct/tnil.nim15
-rw-r--r--tests/effects/teffects4.nim2
-rw-r--r--tests/enum/toptions.nim2
-rw-r--r--tests/errmsgs/t1154.nim11
-rw-r--r--tests/errmsgs/tcant_overload_by_return_type.nim9
-rw-r--r--tests/errmsgs/tinvalidinout.nim8
-rw-r--r--tests/errmsgs/tproper_stacktrace2.nim4
-rw-r--r--tests/exprs/tstmtexprs.nim2
-rw-r--r--tests/flags/tgenscript.nim5
-rw-r--r--tests/generics/module_with_generics.nim14
-rw-r--r--tests/generics/t5602_inheritence.nim8
-rw-r--r--tests/iter/tobj_iter.nim2
-rw-r--r--tests/lexer/tunderscores.nim2
-rw-r--r--tests/macros/tmacro1.nim17
-rw-r--r--tests/macros/tstructuredlogging.nim154
-rw-r--r--tests/macros/ttemplatesymbols.nim173
-rw-r--r--tests/manyloc/argument_parser/argument_parser.nim12
-rw-r--r--tests/manyloc/keineschweine/dependencies/chipmunk/chipmunk.nim27
-rw-r--r--tests/manyloc/keineschweine/dependencies/enet/enet.nim23
-rw-r--r--tests/manyloc/keineschweine/dependencies/genpacket/genpacket_enet.nim14
-rw-r--r--tests/manyloc/keineschweine/dependencies/genpacket/macro_dsl.nim10
-rw-r--r--tests/manyloc/keineschweine/dependencies/nake/nakefile.nim2
-rw-r--r--tests/manyloc/keineschweine/dependencies/sfml/sfml.nim2
-rw-r--r--tests/manyloc/keineschweine/dependencies/sfml/sfml_colors.nim2
-rw-r--r--tests/manyloc/keineschweine/dependencies/sfml/sfml_vector.nim1
-rw-r--r--tests/manyloc/keineschweine/keineschweine.nim2
-rw-r--r--tests/manyloc/keineschweine/lib/estreams.nim2
-rw-r--r--tests/manyloc/keineschweine/lib/input_helpers.nim6
-rw-r--r--tests/manyloc/keineschweine/lib/sg_assets.nim90
-rw-r--r--tests/manyloc/keineschweine/lib/sg_gui.nim2
-rw-r--r--tests/manyloc/keineschweine/lib/sg_packets.nim6
-rw-r--r--tests/manyloc/keineschweine/server/nim.cfg1
-rw-r--r--tests/manyloc/nake/nake.nim2
-rw-r--r--tests/manyloc/nake/nakefile.nim6
-rw-r--r--tests/manyloc/standalone/barebone.nim.cfg1
-rw-r--r--tests/metatype/tbindtypedesc.nim8
-rw-r--r--tests/method/tsimmeth.nim2
-rw-r--r--tests/misc/tmemoization.nim2
-rw-r--r--tests/misc/tsemfold.nim23
-rw-r--r--tests/misc/tsimplesort.nim10
-rw-r--r--tests/misc/tunsignedmisc.nim2
-rw-r--r--tests/modules/definitions.nim4
-rw-r--r--tests/modules/proxy_module.nim3
-rw-r--r--tests/notnil/tmust_compile.nim1
-rw-r--r--tests/notnil/tnotnil.nim6
-rw-r--r--tests/notnil/tnotnil1.nim6
-rw-r--r--tests/notnil/tnotnil2.nim4
-rw-r--r--tests/notnil/tnotnil3.nim2
-rw-r--r--tests/notnil/tnotnil4.nim2
-rw-r--r--tests/notnil/tnotnil_in_generic.nim1
-rw-r--r--tests/notnil/tnotnil_in_objconstr.nim2
-rw-r--r--tests/objects/tinherentedvalues.nim16
-rw-r--r--tests/objects/tobj_asgn_dont_slice.nim24
-rw-r--r--tests/objects/tobjconstr.nim7
-rw-r--r--tests/objvariant/tcheckedfield1.nim2
-rw-r--r--tests/objvariant/temptycaseobj.nim2
-rw-r--r--tests/osproc/tclose.nim24
-rw-r--r--tests/overload/tissue966.nim2
-rw-r--r--tests/overload/toverprc.nim2
-rw-r--r--tests/overload/tparam_forwarding.nim16
-rw-r--r--tests/parallel/tparfind.nim2
-rw-r--r--tests/parallel/twaitany.nim35
-rw-r--r--tests/parser/tbraces.nim432
-rw-r--r--tests/parser/tcommand_as_expr.nim13
-rw-r--r--tests/parser/tinvcolonlocation1.nim4
-rw-r--r--tests/parser/tinvcolonlocation2.nim4
-rw-r--r--tests/parser/tinvcolonlocation3.nim4
-rw-r--r--tests/parser/twhen_in_enum.nim2
-rw-r--r--tests/parser/twrongcmdsyntax.nim2
-rw-r--r--tests/pragmas/tcustom_pragma.nim10
-rw-r--r--tests/pragmas/tnoreturn.nim7
-rw-r--r--tests/proc/tprocredef.nim4
-rw-r--r--tests/range/tsubrange.nim2
-rw-r--r--tests/rodfiles/deadg.nim3
-rw-r--r--tests/stdlib/tpegs.nim1770
-rw-r--r--tests/stdlib/ttimes.nim19
-rw-r--r--tests/stdlib/twchartoutf8.nim1
-rw-r--r--tests/system/tnilconcats.nim25
-rw-r--r--tests/system/toString.nim56
-rw-r--r--tests/template/i2416.nim1
-rw-r--r--tests/template/t2416.nim2
-rw-r--r--tests/testament/backend.nim2
-rw-r--r--tests/testament/categories.nim28
-rw-r--r--tests/testament/htmlgen.nim2
-rw-r--r--tests/testament/tester.nim33
-rw-r--r--tests/threads/tthreadvars.nim78
-rw-r--r--tests/trmacros/thoist.nim2
-rw-r--r--tests/typerel/t7600_1.nim18
-rw-r--r--tests/typerel/t7600_2.nim17
-rw-r--r--tests/typerel/texplicitcmp.nim4
-rw-r--r--tests/typerel/ttypelessemptyset.nim2
-rw-r--r--tests/types/tparameterizedparent3.nim2
-rw-r--r--tests/types/tparameterizedparent4.nim2
-rw-r--r--tests/varres/tnewseq_on_result_vart.nim9
-rw-r--r--tests/vm/tref.nim12
-rw-r--r--tools/finish.nim56
-rw-r--r--tools/nim.bash-completion4
-rw-r--r--tools/nim.zsh-completion2
-rw-r--r--tools/nimgrep.nim40
-rw-r--r--tools/niminst/buildsh.tmpl4
-rw-r--r--tools/nimpretty.nim4
-rw-r--r--web/website.ini2
342 files changed, 9767 insertions, 13341 deletions
diff --git a/changelog.md b/changelog.md
index f39dc2678..f9daf55d1 100644
--- a/changelog.md
+++ b/changelog.md
@@ -1,4 +1,4 @@
-## v0.X.X - XX/XX/2018
+## v0.19.X - XX/XX/2018
 
 ### Changes affecting backwards compatibility
 
@@ -11,6 +11,16 @@
   to deal with!
 - Indexing into a ``cstring`` for the JS target is now mapped
   to ``charCodeAt``.
+- Assignments that would "slice" an object into its supertype are now prevented
+  at runtime. Use ``ref object`` with inheritance rather than ``object`` with
+  inheritance to prevent this issue.
+- The ``not nil`` type annotation now has to be enabled explicitly
+  via ``{.experimental: "notnil"}`` as we are still not pleased with how this
+  feature works with Nim's containers.
+- The parser now warns about inconsistent spacing around binary operators as
+  these can easily be confused with unary operators. This warning will likely
+  become an error in the future.
+
 
 #### Breaking changes in the standard library
 
@@ -27,9 +37,13 @@
 - ``proc `-`*(a, b: Time): int64`` in the ``times`` module has changed return type
   to ``times.Duration`` in order to support higher time resolutions.
   The proc is no longer deprecated.
+- ``posix.Timeval.tv_sec`` has changed type to ``posix.Time``.
 
 #### Breaking changes in the compiler
 
+- The undocumented ``#? braces`` parsing mode was removed.
+- The undocumented PHP backend was removed.
+
 ### Library additions
 
 - ``re.split`` now also supports the ``maxsplit`` parameter for consistency
@@ -55,6 +69,12 @@
   fields.
 - ``system.SomeReal`` is now called ``SomeFloat`` for consistency and
   correctness.
+- ``algorithm.smartBinarySearch`` and ``algorithm.binarySearch`` is
+  now joined in ``binarySearch``. ``smartbinarySearch`` is now
+  deprecated.
+- The `terminal` module now exports additional procs for generating ANSI color
+  codes as strings.
+- Added the parameter ``val`` for the ``CritBitTree[int].inc`` proc.
 
 ### Language additions
 
@@ -76,6 +96,21 @@
   Imported exceptions can be raised and caught just like Nim exceptions.
   More details in language manual.
 
+- ``nil`` for strings/seqs is finally gone. Instead the default value for
+  these is ``"" / @[]``.
+
+- Accessing the binary zero terminator in Nim's native strings
+  is now invalid. Internally a Nim string still has the trailing zero for
+  zero-copy interoperability with ``cstring``. Compile your code with the
+  new switch ``--laxStrings:on`` if you need a transition period.
+
+- The command syntax now supports keyword arguments after the first comma.
+
+- Thread-local variables can now be declared inside procs. This implies all
+  the effects of the `global` pragma.
+
+- Nim now supports `except` clause in the export statement.
+
 ### Tool changes
 
 - ``jsondoc2`` has been renamed ``jsondoc``, similar to how ``doc2`` was renamed
@@ -97,4 +132,10 @@
 - Added ``macros.getProjectPath`` and ``ospaths.putEnv`` procs to Nim's virtual
   machine.
 
+- The ``deadCodeElim`` option is now always turned on and the switch has no
+  effect anymore, but is recognized for backwards compatibility.
+
+- ``experimental`` is now a pragma / command line switch that can enable specific
+  language extensions, it is not an all-or-nothing switch anymore.
+
 ### Bugfixes
diff --git a/compiler/aliases.nim b/compiler/aliases.nim
index 490ac987a..f79210dd7 100644
--- a/compiler/aliases.nim
+++ b/compiler/aliases.nim
@@ -34,10 +34,10 @@ proc isPartOfAux(n: PNode, b: PType, marker: var IntSet): TAnalysisResult =
       of nkOfBranch, nkElse:
         result = isPartOfAux(lastSon(n.sons[i]), b, marker)
         if result == arYes: return
-      else: internalError("isPartOfAux(record case branch)")
+      else: discard "isPartOfAux(record case branch)"
   of nkSym:
     result = isPartOfAux(n.sym.typ, b, marker)
-  else: internalError(n.info, "isPartOfAux()")
+  else: discard
 
 proc isPartOfAux(a, b: PType, marker: var IntSet): TAnalysisResult =
   result = arNo
diff --git a/compiler/ast.nim b/compiler/ast.nim
index da7e828f2..5a84b2b02 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -262,7 +262,8 @@ type
                       # variable is a thread variable
     sfCompileTime,    # proc can be evaluated at compile time
     sfConstructor,    # proc is a C++ constructor
-    sfDeadCodeElim,   # dead code elimination for the module is turned on
+    sfDispatcher,     # copied method symbol is the dispatcher
+                      # deprecated and unused, except for the con
     sfBorrow,         # proc is borrowed
     sfInfixCall,      # symbol needs infix call syntax in target language;
                       # for interfacing with C++, JS
@@ -275,10 +276,9 @@ type
   TSymFlags* = set[TSymFlag]
 
 const
-  sfDispatcher* = sfDeadCodeElim # copied method symbol is the dispatcher
   sfNoInit* = sfMainModule       # don't generate code to init the variable
 
-  sfImmediate* = sfDeadCodeElim
+  sfImmediate* = sfDispatcher
     # macro or template is immediately expanded
     # without considering any possible overloads
   sfAllUntyped* = sfVolatile # macro or template is immediately expanded \
@@ -734,7 +734,7 @@ type
     locOther                  # location is something other
   TLocFlag* = enum
     lfIndirect,               # backend introduced a pointer
-    lfFullExternalName, # only used when 'gCmd == cmdPretty': Indicates
+    lfFullExternalName, # only used when 'conf.cmd == cmdPretty': Indicates
       # that the symbol has been imported via 'importc: "fullname"' and
       # no format string.
     lfNoDeepCopy,             # no need for a deep copy
@@ -1042,9 +1042,9 @@ proc newNode*(kind: TNodeKind): PNode =
   new(result)
   result.kind = kind
   #result.info = UnknownLineInfo() inlined:
-  result.info.fileIndex = int32(-1)
+  result.info.fileIndex = InvalidFileIdx
   result.info.col = int16(-1)
-  result.info.line = int16(-1)
+  result.info.line = uint16(0)
   when defined(useNodeIds):
     result.id = gNodeId
     if result.id == nodeIdToDebug:
@@ -1078,14 +1078,14 @@ template previouslyInferred*(t: PType): PType =
   if t.sons.len > 1: t.lastSon else: nil
 
 proc newSym*(symKind: TSymKind, name: PIdent, owner: PSym,
-             info: TLineInfo): PSym =
+             info: TLineInfo; options: TOptions = {}): PSym =
   # generates a symbol and initializes the hash field too
   new(result)
   result.name = name
   result.kind = symKind
   result.flags = {}
   result.info = info
-  result.options = gOptions
+  result.options = options
   result.owner = owner
   result.offset = -1
   result.id = getID()
@@ -1095,7 +1095,7 @@ proc newSym*(symKind: TSymKind, name: PIdent, owner: PSym,
   #  writeStacktrace()
   #  MessageOut(name.s & " has id: " & toString(result.id))
 
-var emptyNode* = newNode(nkEmpty)
+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 =
@@ -1116,13 +1116,13 @@ proc linkTo*(s: PSym, t: PType): PSym {.discardable.} =
   s.typ = t
   result = s
 
-template fileIdx*(c: PSym): int32 =
+template fileIdx*(c: PSym): FileIndex =
   # XXX: this should be used only on module symbols
-  c.position.int32
+  c.position.FileIndex
 
 template filename*(c: PSym): string =
   # XXX: this should be used only on module symbols
-  c.position.int32.toFilename
+  c.position.FileIndex.toFilename
 
 proc appendToModule*(m: PSym, n: PNode) =
   ## The compiler will use this internally to add nodes that will be
@@ -1325,7 +1325,7 @@ proc copyType*(t: PType, owner: PSym, keepId: bool): PType =
 proc exactReplica*(t: PType): PType = copyType(t, t.owner, true)
 
 proc copySym*(s: PSym, keepId: bool = false): PSym =
-  result = newSym(s.kind, s.name, s.owner, s.info)
+  result = newSym(s.kind, s.name, s.owner, s.info, s.options)
   #result.ast = nil            # BUGFIX; was: s.ast which made problems
   result.typ = s.typ
   if keepId:
@@ -1344,8 +1344,9 @@ proc copySym*(s: PSym, keepId: bool = false): PSym =
   if result.kind in {skVar, skLet, skField}:
     result.guard = s.guard
 
-proc createModuleAlias*(s: PSym, newIdent: PIdent, info: TLineInfo): PSym =
-  result = newSym(s.kind, newIdent, s.owner, info)
+proc createModuleAlias*(s: PSym, newIdent: PIdent, info: TLineInfo;
+                        options: TOptions): PSym =
+  result = newSym(s.kind, newIdent, s.owner, info, options)
   # keep ID!
   result.ast = s.ast
   result.id = s.id
@@ -1564,15 +1565,17 @@ proc getInt*(a: PNode): BiggestInt =
   case a.kind
   of nkCharLit..nkUInt64Lit: result = a.intVal
   else:
-    internalError(a.info, "getInt")
-    result = 0
+    #internalError(a.info, "getInt")
+    doAssert false, "getInt"
+    #result = 0
 
 proc getFloat*(a: PNode): BiggestFloat =
   case a.kind
   of nkFloatLiterals: result = a.floatVal
   else:
-    internalError(a.info, "getFloat")
-    result = 0.0
+    doAssert false, "getFloat"
+    #internalError(a.info, "getFloat")
+    #result = 0.0
 
 proc getStr*(a: PNode): string =
   case a.kind
@@ -1581,16 +1584,18 @@ proc getStr*(a: PNode): string =
     # let's hope this fixes more problems than it creates:
     result = nil
   else:
-    internalError(a.info, "getStr")
-    result = ""
+    doAssert false, "getStr"
+    #internalError(a.info, "getStr")
+    #result = ""
 
 proc getStrOrChar*(a: PNode): string =
   case a.kind
   of nkStrLit..nkTripleStrLit: result = a.strVal
   of nkCharLit..nkUInt64Lit: result = $chr(int(a.intVal))
   else:
-    internalError(a.info, "getStrOrChar")
-    result = ""
+    doAssert false, "getStrOrChar"
+    #internalError(a.info, "getStrOrChar")
+    #result = ""
 
 proc isGenericRoutine*(s: PSym): bool =
   case s.kind
@@ -1615,6 +1620,19 @@ proc originatingModule*(s: PSym): PSym =
 proc isRoutine*(s: PSym): bool {.inline.} =
   result = s.kind in skProcKinds
 
+proc isCompileTimeProc*(s: PSym): bool {.inline.} =
+  result = s.kind == skMacro or
+           s.kind == skProc and sfCompileTime in s.flags
+
+proc requiredParams*(s: PSym): int =
+  # Returns the number of required params (without default values)
+  # XXX: Perhaps we can store this in the `offset` field of the
+  # symbol instead?
+  for i in 1 ..< s.typ.len:
+    if s.typ.n[i].sym.ast != nil:
+      return i - 1
+  return s.typ.len - 1
+
 proc hasPattern*(s: PSym): bool {.inline.} =
   result = isRoutine(s) and s.ast.sons[patternPos].kind != nkEmpty
 
@@ -1622,7 +1640,7 @@ iterator items*(n: PNode): PNode =
   for i in 0..<n.safeLen: yield n.sons[i]
 
 iterator pairs*(n: PNode): tuple[i: int, n: PNode] =
-  for i in 0..<n.len: yield (i, n.sons[i])
+  for i in 0..<n.safeLen: yield (i, n.sons[i])
 
 proc isAtom*(n: PNode): bool {.inline.} =
   result = n.kind >= nkNone and n.kind <= nkNilLit
@@ -1671,14 +1689,14 @@ proc isException*(t: PType): bool =
 
   var base = t
   while base != nil:
-    if base.sym.magic == mException:
+    if base.sym != nil and base.sym.magic == mException:
       return true
     base = base.lastSon
   return false
 
-proc isImportedException*(t: PType): bool =
+proc isImportedException*(t: PType; conf: ConfigRef): bool =
   assert(t != nil)
-  if optNoCppExceptions in gGlobalOptions:
+  if optNoCppExceptions in conf.globalOptions:
     return false
 
   let base = t.skipTypes({tyAlias, tyPtr, tyDistinct, tyGenericInst})
diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim
index 196ac8690..d98a42b34 100644
--- a/compiler/astalgo.nim
+++ b/compiler/astalgo.nim
@@ -69,22 +69,22 @@ proc debug*(n: PNode) {.deprecated.}
 
 template mdbg*: bool {.dirty.} =
   when compiles(c.module):
-    c.module.fileIdx == gProjectMainIdx
+    c.module.fileIdx.int32 == gProjectMainIdx
   elif compiles(c.c.module):
-    c.c.module.fileIdx == gProjectMainIdx
+    c.c.module.fileIdx.int32 == gProjectMainIdx
   elif compiles(m.c.module):
-    m.c.module.fileIdx == gProjectMainIdx
+    m.c.module.fileIdx.int32 == gProjectMainIdx
   elif compiles(cl.c.module):
-    cl.c.module.fileIdx == gProjectMainIdx
+    cl.c.module.fileIdx.int32 == gProjectMainIdx
   elif compiles(p):
     when compiles(p.lex):
-      p.lex.fileIdx == gProjectMainIdx
+      p.lex.fileIdx.int32 == gProjectMainIdx
     else:
-      p.module.module.fileIdx == gProjectMainIdx
+      p.module.module.fileIdx.int32 == gProjectMainIdx
   elif compiles(m.module.fileIdx):
-    m.module.fileIdx == gProjectMainIdx
+    m.module.fileIdx.int32 == gProjectMainIdx
   elif compiles(L.fileIdx):
-    L.fileIdx == gProjectMainIdx
+    L.fileIdx.int32 == gProjectMainIdx
   else:
     error()
 
@@ -180,7 +180,7 @@ proc lookupInRecord(n: PNode, field: PIdent): PSym =
       result = lookupInRecord(n.sons[i], field)
       if result != nil: return
   of nkRecCase:
-    if (n.sons[0].kind != nkSym): internalError(n.info, "lookupInRecord")
+    if (n.sons[0].kind != nkSym): return nil
     result = lookupInRecord(n.sons[0], field)
     if result != nil: return
     for i in countup(1, sonsLen(n) - 1):
@@ -188,10 +188,10 @@ proc lookupInRecord(n: PNode, field: PIdent): PSym =
       of nkOfBranch, nkElse:
         result = lookupInRecord(lastSon(n.sons[i]), field)
         if result != nil: return
-      else: internalError(n.info, "lookupInRecord(record case branch)")
+      else: return nil
   of nkSym:
     if n.sym.name.id == field.id: result = n.sym
-  else: internalError(n.info, "lookupInRecord()")
+  else: return nil
 
 proc getModule(s: PSym): PSym =
   result = s
@@ -203,7 +203,7 @@ proc getSymFromList(list: PNode, ident: PIdent, start: int = 0): PSym =
     if list.sons[i].kind == nkSym:
       result = list.sons[i].sym
       if result.name.id == ident.id: return
-    else: internalError(list.info, "getSymFromList")
+    else: return nil
   result = nil
 
 proc hashNode(p: RootRef): Hash =
diff --git a/compiler/bitsets.nim b/compiler/bitsets.nim
index 6afd1bd78..e38732877 100644
--- a/compiler/bitsets.nim
+++ b/compiler/bitsets.nim
@@ -72,24 +72,25 @@ proc bitSetContains(x, y: TBitSet): bool =
   result = true
 
 # Number of set bits for all values of int8
-const populationCount: array[low(int8)..high(int8), int8] = [
-  1.int8, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 
-  2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 
-  2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 
-  3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 
-  2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 
-  3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 
-  3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 
-  4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 
-  0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 
-  1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 
-  1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 
-  2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 
-  1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 
-  2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 
-  2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 
-  3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7
-]
+const populationCount: array[low(int8)..high(int8), int8] = block:
+    var arr: array[low(int8)..high(int8), int8]
+
+    proc countSetBits(x: int8): int8 =
+      return
+        ( x and 0b00000001'i8) +
+        ((x and 0b00000010'i8) shr 1) +
+        ((x and 0b00000100'i8) shr 2) +
+        ((x and 0b00001000'i8) shr 3) +
+        ((x and 0b00010000'i8) shr 4) +
+        ((x and 0b00100000'i8) shr 5) +
+        ((x and 0b01000000'i8) shr 6) +
+        ((x and 0b10000000'i8) shr 7)
+        
+
+    for it in low(int8)..high(int8):
+      arr[it] = countSetBits(it)
+
+    arr
 
 proc bitSetCard(x: TBitSet): BiggestInt =
   for it in x:
diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim
index 5f14b6804..7d355db5f 100644
--- a/compiler/ccgcalls.nim
+++ b/compiler/ccgcalls.nim
@@ -116,7 +116,7 @@ proc openArrayLoc(p: BProc, n: PNode): Rope =
       else:
         result = "$1->data+($2), ($3)-($2)+1" % [rdLoc(a), rdLoc(b), rdLoc(c)]
     else:
-      internalError("openArrayLoc: " & typeToString(a.t))
+      internalError(p.config, "openArrayLoc: " & typeToString(a.t))
   else:
     initLocExpr(p, n, a)
     case skipTypes(a.t, abstractVar).kind
@@ -125,25 +125,25 @@ proc openArrayLoc(p: BProc, n: PNode): Rope =
     of tyString, tySequence:
       if skipTypes(n.typ, abstractInst).kind == tyVar and
             not compileToCpp(p.module):
-        result = "(*$1)->data, (*$1)->$2" % [a.rdLoc, lenField(p)]
+        result = "(*$1)->data, (*$1 ? (*$1)->$2 : 0)" % [a.rdLoc, lenField(p)]
       else:
-        result = "$1->data, $1->$2" % [a.rdLoc, lenField(p)]
+        result = "$1->data, ($1 ? $1->$2 : 0)" % [a.rdLoc, lenField(p)]
     of tyArray:
       result = "$1, $2" % [rdLoc(a), rope(lengthOrd(a.t))]
     of tyPtr, tyRef:
       case lastSon(a.t).kind
       of tyString, tySequence:
-        result = "(*$1)->data, (*$1)->$2" % [a.rdLoc, lenField(p)]
+        result = "(*$1)->data, (*$1 ? (*$1)->$2 : 0)" % [a.rdLoc, lenField(p)]
       of tyArray:
         result = "$1, $2" % [rdLoc(a), rope(lengthOrd(lastSon(a.t)))]
       else:
-        internalError("openArrayLoc: " & typeToString(a.t))
-    else: internalError("openArrayLoc: " & typeToString(a.t))
+        internalError(p.config, "openArrayLoc: " & typeToString(a.t))
+    else: internalError(p.config, "openArrayLoc: " & typeToString(a.t))
 
 proc genArgStringToCString(p: BProc, n: PNode): Rope {.inline.} =
   var a: TLoc
   initLocExpr(p, n.sons[0], a)
-  result = "$1->data" % [a.rdLoc]
+  result = "($1 ? $1->data : (NCSTRING)\"\")" % [a.rdLoc]
 
 proc genArg(p: BProc, n: PNode, param: PSym; call: PNode): Rope =
   var a: TLoc
@@ -273,7 +273,7 @@ proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType): Rope =
       result = genArgNoParam(p, ri.sons[i]) #, typ.n.sons[i].sym)
   else:
     if tfVarargs notin typ.flags:
-      localError(ri.info, "wrong argument count")
+      localError(p.config, ri.info, "wrong argument count")
       result = nil
     else:
       result = genArgNoParam(p, ri.sons[i])
@@ -337,7 +337,7 @@ proc genThisArg(p: BProc; ri: PNode; i: int; typ: PType): Rope =
   # for better or worse c2nim translates the 'this' argument to a 'var T'.
   # However manual wrappers may also use 'ptr T'. In any case we support both
   # for convenience.
-  internalAssert i < sonsLen(typ)
+  internalAssert p.config, i < sonsLen(typ)
   assert(typ.n.sons[i].kind == nkSym)
   # if the parameter is lying (tyVar) and thus we required an additional deref,
   # skip the deref:
@@ -394,7 +394,7 @@ proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType): Rope =
             result.add genOtherArg(p, ri, k, typ)
           result.add(~")")
         else:
-          localError(ri.info, "call expression expected for C++ pattern")
+          localError(p.config, ri.info, "call expression expected for C++ pattern")
         inc i
       elif pat[i+1] == '.':
         result.add genThisArg(p, ri, j, typ)
@@ -432,7 +432,7 @@ proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) =
   assert(sonsLen(typ) == sonsLen(typ.n))
   # don't call '$' here for efficiency:
   let pat = ri.sons[0].sym.loc.r.data
-  internalAssert pat != nil
+  internalAssert p.config, pat != nil
   if pat.contains({'#', '(', '@', '\''}):
     var pl = genPatternCall(p, ri, pat, typ)
     # simpler version of 'fixupCall' that works with the pl+params combination:
@@ -481,7 +481,7 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
 
   # don't call '$' here for efficiency:
   let pat = ri.sons[0].sym.loc.r.data
-  internalAssert pat != nil
+  internalAssert p.config, pat != nil
   var start = 3
   if ' ' in pat:
     start = 1
@@ -501,7 +501,7 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
   for i in countup(start, length-1):
     assert(sonsLen(typ) == sonsLen(typ.n))
     if i >= sonsLen(typ):
-      internalError(ri.info, "varargs for objective C method?")
+      internalError(p.config, ri.info, "varargs for objective C method?")
     assert(typ.n.sons[i].kind == nkSym)
     var param = typ.n.sons[i].sym
     add(pl, ~" ")
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index 7e3c2632a..335aa2f84 100644
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -13,7 +13,7 @@
 
 proc int64Literal(i: BiggestInt): Rope =
   if i > low(int64):
-    result = rfmt(nil, "IL64($1)", rope(i))
+    result = "IL64($1)" % [rope(i)]
   else:
     result = ~"(IL64(-9223372036854775807) - IL64(1))"
 
@@ -26,12 +26,12 @@ proc intLiteral(i: BiggestInt): Rope =
     # Nim has the same bug for the same reasons :-)
     result = ~"(-2147483647 -1)"
   elif i > low(int64):
-    result = rfmt(nil, "IL64($1)", rope(i))
+    result = "IL64($1)" % [rope(i)]
   else:
     result = ~"(IL64(-9223372036854775807) - IL64(1))"
 
 proc genLiteral(p: BProc, n: PNode, ty: PType): Rope =
-  if ty == nil: internalError(n.info, "genLiteral: ty is nil")
+  if ty == nil: internalError(p.config, n.info, "genLiteral: ty is nil")
   case n.kind
   of nkCharLit..nkUInt64Lit:
     case skipTypes(ty, abstractVarRange).kind
@@ -63,6 +63,10 @@ proc genLiteral(p: BProc, n: PNode, ty: PType): Rope =
     of tyNil:
       result = genNilStringLiteral(p.module, n.info)
     of tyString:
+      # with the new semantics for 'nil' strings, we can map "" to nil and
+      # save tons of allocations:
+      #if n.strVal.len == 0: result = genNilStringLiteral(p.module, n.info)
+      #else:
       result = genStringLiteral(p.module, n)
     else:
       if n.strVal.isNil: result = rope("NIM_NIL")
@@ -72,7 +76,7 @@ proc genLiteral(p: BProc, n: PNode, ty: PType): Rope =
   of nkFloat32Lit:
     result = rope(n.floatVal.toStrMaxPrecision("f"))
   else:
-    internalError(n.info, "genLiteral(" & $n.kind & ')')
+    internalError(p.config, n.info, "genLiteral(" & $n.kind & ')')
     result = nil
 
 proc genLiteral(p: BProc, n: PNode): Rope =
@@ -141,7 +145,7 @@ proc getStorageLoc(n: PNode): TStorageLoc =
     of tyVar, tyLent: result = OnUnknown
     of tyPtr: result = OnStack
     of tyRef: result = OnHeap
-    else: internalError(n.info, "getStorageLoc")
+    else: doAssert(false, "getStorageLoc")
   of nkBracketExpr, nkDotExpr, nkObjDownConv, nkObjUpConv:
     result = getStorageLoc(n.sons[0])
   else: result = OnUnknown
@@ -160,7 +164,7 @@ proc canMove(n: PNode): bool =
   #  result = false
 
 proc genRefAssign(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
-  if dest.storage == OnStack or not usesNativeGC():
+  if dest.storage == OnStack or not usesNativeGC(p.config):
     linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src))
   elif dest.storage == OnHeap:
     # location is on heap
@@ -252,7 +256,7 @@ proc genGenericAsgn(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
   # (for objects, etc.):
   if needToCopy notin flags or
       tfShallow in skipTypes(dest.t, abstractVarRange).flags:
-    if dest.storage == OnStack or not usesNativeGC():
+    if dest.storage == OnStack or not usesNativeGC(p.config):
       useStringh(p.module)
       linefmt(p, cpsStmts,
            "memcpy((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n",
@@ -286,7 +290,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
     if (needToCopy notin flags and src.storage != OnStatic) or canMove(src.lode):
       genRefAssign(p, dest, src, flags)
     else:
-      if dest.storage == OnStack or not usesNativeGC():
+      if dest.storage == OnStack or not usesNativeGC(p.config):
         linefmt(p, cpsStmts, "$1 = #copyString($2);$n", dest.rdLoc, src.rdLoc)
       elif dest.storage == OnHeap:
         # we use a temporary to care for the dreaded self assignment:
@@ -322,7 +326,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
     elif needsComplexAssignment(ty):
       if ty.sons[0].isNil and asgnComplexity(ty.n) <= 4:
         discard getTypeDesc(p.module, ty)
-        internalAssert ty.n != nil
+        internalAssert p.config, ty.n != nil
         genOptAsgnObject(p, dest, src, flags, ty.n, ty)
       else:
         genGenericAsgn(p, dest, src, flags)
@@ -359,7 +363,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
   of tyPtr, tyPointer, tyChar, tyBool, tyEnum, tyCString,
      tyInt..tyUInt64, tyRange, tyVar, tyLent:
     linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src))
-  else: internalError("genAssignment: " & $ty.kind)
+  else: internalError(p.config, "genAssignment: " & $ty.kind)
 
   if optMemTracker in p.options and dest.storage in {OnHeap, OnUnknown}:
     #writeStackTrace()
@@ -405,7 +409,7 @@ proc genDeepCopy(p: BProc; dest, src: TLoc) =
   of tyPointer, tyChar, tyBool, tyEnum, tyCString,
      tyInt..tyUInt64, tyRange, tyVar, tyLent:
     linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src))
-  else: internalError("genDeepCopy: " & $ty.kind)
+  else: internalError(p.config, "genDeepCopy: " & $ty.kind)
 
 proc putLocIntoDest(p: BProc, d: var TLoc, s: TLoc) =
   if d.k != locNone:
@@ -446,14 +450,14 @@ proc putIntoDest(p: BProc, d: var TLoc, n: PNode, r: Rope; s=OnUnknown) =
 
 proc binaryStmt(p: BProc, e: PNode, d: var TLoc, frmt: string) =
   var a, b: TLoc
-  if d.k != locNone: internalError(e.info, "binaryStmt")
+  if d.k != locNone: internalError(p.config, e.info, "binaryStmt")
   initLocExpr(p, e.sons[1], a)
   initLocExpr(p, e.sons[2], b)
   lineCg(p, cpsStmts, frmt, rdLoc(a), rdLoc(b))
 
 proc unaryStmt(p: BProc, e: PNode, d: var TLoc, frmt: string) =
   var a: TLoc
-  if d.k != locNone: internalError(e.info, "unaryStmt")
+  if d.k != locNone: internalError(p.config, e.info, "unaryStmt")
   initLocExpr(p, e.sons[1], a)
   lineCg(p, cpsStmts, frmt, [rdLoc(a)])
 
@@ -697,7 +701,7 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false) =
       of tyPtr:
         d.storage = OnUnknown         # BUGFIX!
       else:
-        internalError(e.info, "genDeref " & $typ.kind)
+        internalError(p.config, e.info, "genDeref " & $typ.kind)
     elif p.module.compileToCpp:
       if typ.kind == tyVar and tfVarIsPtr notin typ.flags and
            e.kind == nkHiddenDeref:
@@ -732,7 +736,7 @@ template inheritLocation(d: var TLoc, a: TLoc) =
 
 proc genRecordFieldAux(p: BProc, e: PNode, d, a: var TLoc) =
   initLocExpr(p, e.sons[0], a)
-  if e.sons[1].kind != nkSym: internalError(e.info, "genRecordFieldAux")
+  if e.sons[1].kind != nkSym: internalError(p.config, e.info, "genRecordFieldAux")
   d.inheritLocation(a)
   discard getTypeDesc(p.module, a.t) # fill the record's fields.loc
 
@@ -748,7 +752,7 @@ proc genTupleElem(p: BProc, e: PNode, d: var TLoc) =
   var r = rdLoc(a)
   case e.sons[1].kind
   of nkIntLit..nkUInt64Lit: i = int(e.sons[1].intVal)
-  else: internalError(e.info, "genTupleElem")
+  else: internalError(p.config, e.info, "genTupleElem")
   addf(r, ".Field$1", [rope(i)])
   putIntoDest(p, d, e, r, a.storage)
 
@@ -765,7 +769,7 @@ proc lookupFieldAgain(p: BProc, ty: PType; field: PSym; r: var Rope;
       break
     if not p.module.compileToCpp: add(r, ".Sup")
     ty = ty.sons[0]
-  if result == nil: internalError(field.info, "genCheckedRecordField")
+  if result == nil: internalError(p.config, field.info, "genCheckedRecordField")
 
 proc genRecordField(p: BProc, e: PNode, d: var TLoc) =
   var a: TLoc
@@ -782,7 +786,7 @@ proc genRecordField(p: BProc, e: PNode, d: var TLoc) =
     var rtyp: PType
     let field = lookupFieldAgain(p, ty, f, r, addr rtyp)
     if field.loc.r == nil and rtyp != nil: fillObjectFields(p.module, rtyp)
-    if field.loc.r == nil: internalError(e.info, "genRecordField 3 " & typeToString(ty))
+    if field.loc.r == nil: internalError(p.config, e.info, "genRecordField 3 " & typeToString(ty))
     addf(r, ".$1", [field.loc.r])
     putIntoDest(p, d, e, r, a.storage)
 
@@ -828,9 +832,9 @@ proc genCheckedRecordField(p: BProc, e: PNode, d: var TLoc) =
     let field = lookupFieldAgain(p, ty, f, r)
     if field.loc.r == nil: fillObjectFields(p.module, ty)
     if field.loc.r == nil:
-      internalError(e.info, "genCheckedRecordField") # generate the checks:
+      internalError(p.config, e.info, "genCheckedRecordField") # generate the checks:
     genFieldCheck(p, e, r, field)
-    add(r, rfmt(nil, ".$1", field.loc.r))
+    add(r, ropecg(p.module, ".$1", field.loc.r))
     putIntoDest(p, d, e.sons[0], r, a.storage)
   else:
     genRecordField(p, e.sons[0], d)
@@ -855,10 +859,10 @@ proc genArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) =
     else:
       let idx = getOrdValue(y)
       if idx < firstOrd(ty) or idx > lastOrd(ty):
-        localError(x.info, errIndexOutOfBounds)
+        localError(p.config, x.info, "index out of bounds")
   d.inheritLocation(a)
   putIntoDest(p, d, n,
-              rfmt(nil, "$1[($2)- $3]", rdLoc(a), rdCharLoc(b), first), a.storage)
+              ropecg(p.module, "$1[($2)- $3]", rdLoc(a), rdCharLoc(b), first), a.storage)
 
 proc genCStringElem(p: BProc, n, x, y: PNode, d: var TLoc) =
   var a, b: TLoc
@@ -867,7 +871,7 @@ proc genCStringElem(p: BProc, n, x, y: PNode, d: var TLoc) =
   var ty = skipTypes(a.t, abstractVarRange)
   inheritLocation(d, a)
   putIntoDest(p, d, n,
-              rfmt(nil, "$1[$2]", rdLoc(a), rdCharLoc(b)), a.storage)
+              ropecg(p.module, "$1[$2]", rdLoc(a), rdCharLoc(b)), a.storage)
 
 proc genIndexCheck(p: BProc; arr, idx: TLoc) =
   let ty = skipTypes(arr.t, abstractVarRange)
@@ -882,7 +886,7 @@ proc genIndexCheck(p: BProc; arr, idx: TLoc) =
               rdCharLoc(idx), first, intLiteral(lastOrd(ty)))
   of tySequence, tyString:
     linefmt(p, cpsStmts,
-          "if ((NU)($1) >= (NU)($2->$3)) #raiseIndexError();$n",
+          "if (!$2 || (NU)($1) >= (NU)($2->$3)) #raiseIndexError();$n",
           rdLoc(idx), rdLoc(arr), lenField(p))
   else: discard
 
@@ -895,7 +899,7 @@ proc genOpenArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) =
             rdLoc(b), rdLoc(a)) # BUGFIX: ``>=`` and not ``>``!
   inheritLocation(d, a)
   putIntoDest(p, d, n,
-              rfmt(nil, "$1[$2]", rdLoc(a), rdCharLoc(b)), a.storage)
+              ropecg(p.module, "$1[$2]", rdLoc(a), rdCharLoc(b)), a.storage)
 
 proc genSeqElem(p: BProc, n, x, y: PNode, d: var TLoc) =
   var a, b: TLoc
@@ -905,19 +909,19 @@ proc genSeqElem(p: BProc, n, x, y: PNode, d: var TLoc) =
   if ty.kind in {tyRef, tyPtr}:
     ty = skipTypes(ty.lastSon, abstractVarRange) # emit range check:
   if optBoundsCheck in p.options:
-    if ty.kind == tyString:
+    if ty.kind == tyString and (not defined(nimNoZeroTerminator) or optLaxStrings in p.options):
       linefmt(p, cpsStmts,
-           "if ((NU)($1) > (NU)($2->$3)) #raiseIndexError();$n",
-           rdLoc(b), rdLoc(a), lenField(p))
+              "if (!$2 || (NU)($1) > (NU)($2->$3)) #raiseIndexError();$n",
+              rdLoc(b), rdLoc(a), lenField(p))
     else:
       linefmt(p, cpsStmts,
-           "if ((NU)($1) >= (NU)($2->$3)) #raiseIndexError();$n",
-           rdLoc(b), rdLoc(a), lenField(p))
+              "if (!$2 || (NU)($1) >= (NU)($2->$3)) #raiseIndexError();$n",
+              rdLoc(b), rdLoc(a), lenField(p))
   if d.k == locNone: d.storage = OnHeap
   if skipTypes(a.t, abstractVar).kind in {tyRef, tyPtr}:
-    a.r = rfmt(nil, "(*$1)", a.r)
+    a.r = ropecg(p.module, "(*$1)", a.r)
   putIntoDest(p, d, n,
-              rfmt(nil, "$1->data[$2]", rdLoc(a), rdCharLoc(b)), a.storage)
+              ropecg(p.module, "$1->data[$2]", rdLoc(a), rdCharLoc(b)), a.storage)
 
 proc genBracketExpr(p: BProc; n: PNode; d: var TLoc) =
   var ty = skipTypes(n.sons[0].typ, abstractVarRange + tyUserTypeClasses)
@@ -928,7 +932,7 @@ proc genBracketExpr(p: BProc; n: PNode; d: var TLoc) =
   of tySequence, tyString: genSeqElem(p, n, n.sons[0], n.sons[1], d)
   of tyCString: genCStringElem(p, n, n.sons[0], n.sons[1], d)
   of tyTuple: genTupleElem(p, n, d)
-  else: internalError(n.info, "expr(nkBracketExpr, " & $ty.kind & ')')
+  else: internalError(p.config, n.info, "expr(nkBracketExpr, " & $ty.kind & ')')
 
 proc genAndOr(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
   # how to generate code?
@@ -973,17 +977,17 @@ proc genAndOr(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
 proc genEcho(p: BProc, n: PNode) =
   # this unusal way of implementing it ensures that e.g. ``echo("hallo", 45)``
   # is threadsafe.
-  internalAssert n.kind == nkBracket
+  internalAssert p.config, n.kind == nkBracket
   if platform.targetOS == osGenode:
     # bypass libc and print directly to the Genode LOG session
     var args: Rope = nil
     var a: TLoc
     for it in n.sons:
       if it.skipConv.kind == nkNilLit:
-        add(args, ", \"nil\"")
+        add(args, ", \"\"")
       else:
         initLocExpr(p, it, a)
-        addf(args, ", $1? ($1)->data:\"nil\"", [rdLoc(a)])
+        addf(args, ", $1? ($1)->data:\"\"", [rdLoc(a)])
     p.module.includeHeader("<base/log.h>")
     linefmt(p, cpsStmts, """Genode::log(""$1);$n""", args)
   else:
@@ -999,8 +1003,8 @@ proc genEcho(p: BProc, n: PNode) =
               makeCString(repeat("%s", n.len) & tnl), args)
       linefmt(p, cpsStmts, "fflush(stdout);$n")
 
-proc gcUsage(n: PNode) =
-  if gSelectedGC == gcNone: message(n.info, warnGcMem, n.renderTree)
+proc gcUsage(conf: ConfigRef; n: PNode) =
+  if conf.selectedGC == gcNone: message(conf, n.info, warnGcMem, n.renderTree)
 
 proc genStrConcat(p: BProc, e: PNode, d: var TLoc) =
   #   <Nim code>
@@ -1029,20 +1033,20 @@ proc genStrConcat(p: BProc, e: PNode, d: var TLoc) =
     initLocExpr(p, e.sons[i + 1], a)
     if skipTypes(e.sons[i + 1].typ, abstractVarRange).kind == tyChar:
       inc(L)
-      add(appends, rfmt(p.module, "#appendChar($1, $2);$n", tmp.r, rdLoc(a)))
+      add(appends, ropecg(p.module, "#appendChar($1, $2);$n", tmp.r, rdLoc(a)))
     else:
       if e.sons[i + 1].kind in {nkStrLit..nkTripleStrLit}:
         inc(L, len(e.sons[i + 1].strVal))
       else:
-        addf(lens, "$1->$2 + ", [rdLoc(a), lenField(p)])
-      add(appends, rfmt(p.module, "#appendString($1, $2);$n", tmp.r, rdLoc(a)))
+        addf(lens, "($1 ? $1->$2 : 0) + ", [rdLoc(a), lenField(p)])
+      add(appends, ropecg(p.module, "#appendString($1, $2);$n", tmp.r, rdLoc(a)))
   linefmt(p, cpsStmts, "$1 = #rawNewString($2$3);$n", tmp.r, lens, rope(L))
   add(p.s(cpsStmts), appends)
   if d.k == locNone:
     d = tmp
   else:
     genAssignment(p, d, tmp, {}) # no need for deep copying
-  gcUsage(e)
+  gcUsage(p.config, e)
 
 proc genStrAppend(p: BProc, e: PNode, d: var TLoc) =
   #  <Nim code>
@@ -1067,44 +1071,44 @@ proc genStrAppend(p: BProc, e: PNode, d: var TLoc) =
     initLocExpr(p, e.sons[i + 2], a)
     if skipTypes(e.sons[i + 2].typ, abstractVarRange).kind == tyChar:
       inc(L)
-      add(appends, rfmt(p.module, "#appendChar($1, $2);$n",
+      add(appends, ropecg(p.module, "#appendChar($1, $2);$n",
                         rdLoc(dest), rdLoc(a)))
     else:
       if e.sons[i + 2].kind in {nkStrLit..nkTripleStrLit}:
         inc(L, len(e.sons[i + 2].strVal))
       else:
-        addf(lens, "$1->$2 + ", [rdLoc(a), lenField(p)])
-      add(appends, rfmt(p.module, "#appendString($1, $2);$n",
+        addf(lens, "($1 ? $1->$2 : 0) + ", [rdLoc(a), lenField(p)])
+      add(appends, ropecg(p.module, "#appendString($1, $2);$n",
                         rdLoc(dest), rdLoc(a)))
   linefmt(p, cpsStmts, "$1 = #resizeString($1, $2$3);$n",
           rdLoc(dest), lens, rope(L))
   add(p.s(cpsStmts), appends)
-  gcUsage(e)
+  gcUsage(p.config, e)
 
 proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) =
   # seq &= x  -->
   #    seq = (typeof seq) incrSeq(&seq->Sup, sizeof(x));
   #    seq->data[seq->len-1] = x;
   let seqAppendPattern = if not p.module.compileToCpp:
-                           "$1 = ($2) #incrSeqV2(&($1)->Sup, sizeof($3));$n"
+                           "$1 = ($2) #incrSeqV3(&($1)->Sup, $3);$n"
                          else:
-                           "$1 = ($2) #incrSeqV2($1, sizeof($3));$n"
+                           "$1 = ($2) #incrSeqV3($1, $3);$n"
   var a, b, dest, tmpL: TLoc
   initLocExpr(p, e.sons[1], a)
   initLocExpr(p, e.sons[2], b)
-  let bt = skipTypes(e.sons[2].typ, {tyVar})
+  let seqType = skipTypes(e.sons[1].typ, {tyVar})
   lineCg(p, cpsStmts, seqAppendPattern, [
       rdLoc(a),
       getTypeDesc(p.module, e.sons[1].typ),
-      getTypeDesc(p.module, bt)])
+      genTypeInfo(p.module, seqType, e.info)])
   #if bt != b.t:
   #  echo "YES ", e.info, " new: ", typeToString(bt), " old: ", typeToString(b.t)
   initLoc(dest, locExpr, e.sons[2], OnHeap)
   getIntTemp(p, tmpL)
   lineCg(p, cpsStmts, "$1 = $2->$3++;$n", tmpL.r, rdLoc(a), lenField(p))
-  dest.r = rfmt(nil, "$1->data[$2]", rdLoc(a), tmpL.r)
+  dest.r = ropecg(p.module, "$1->data[$2]", rdLoc(a), tmpL.r)
   genAssignment(p, dest, b, {needToCopy, afDestIsNil})
-  gcUsage(e)
+  gcUsage(p.config, e)
 
 proc genReset(p: BProc, n: PNode) =
   var a: TLoc
@@ -1127,7 +1131,7 @@ proc rawGenNew(p: BProc, a: TLoc, sizeExpr: Rope) =
   let args = [getTypeDesc(p.module, typ),
               genTypeInfo(p.module, typ, a.lode.info),
               sizeExpr]
-  if a.storage == OnHeap and usesNativeGC():
+  if a.storage == OnHeap and usesNativeGC(p.config):
     # use newObjRC1 as an optimization
     if canFormAcycle(a.t):
       linefmt(p, cpsStmts, "if ($1) { #nimGCunrefRC1($1); $1 = NIM_NIL; }$n", a.rdLoc)
@@ -1150,7 +1154,7 @@ proc genNew(p: BProc, e: PNode) =
     rawGenNew(p, a, se.rdLoc)
   else:
     rawGenNew(p, a, nil)
-  gcUsage(e)
+  gcUsage(p.config, e)
 
 proc genNewSeqAux(p: BProc, dest: TLoc, length: Rope) =
   let seqtype = skipTypes(dest.t, abstractVarRange)
@@ -1158,7 +1162,7 @@ proc genNewSeqAux(p: BProc, dest: TLoc, length: Rope) =
               genTypeInfo(p.module, seqtype, dest.lode.info), length]
   var call: TLoc
   initLoc(call, locExpr, dest.lode, OnHeap)
-  if dest.storage == OnHeap and usesNativeGC():
+  if dest.storage == OnHeap and usesNativeGC(p.config):
     if canFormAcycle(dest.t):
       linefmt(p, cpsStmts, "if ($1) { #nimGCunrefRC1($1); $1 = NIM_NIL; }$n", dest.rdLoc)
     else:
@@ -1174,7 +1178,7 @@ proc genNewSeq(p: BProc, e: PNode) =
   initLocExpr(p, e.sons[1], a)
   initLocExpr(p, e.sons[2], b)
   genNewSeqAux(p, a, b.rdLoc)
-  gcUsage(e)
+  gcUsage(p.config, e)
 
 proc genNewSeqOfCap(p: BProc; e: PNode; d: var TLoc) =
   let seqtype = skipTypes(e.typ, abstractVarRange)
@@ -1184,7 +1188,7 @@ proc genNewSeqOfCap(p: BProc; e: PNode; d: var TLoc) =
               "($1)#nimNewSeqOfCap($2, $3)", [
               getTypeDesc(p.module, seqtype),
               genTypeInfo(p.module, seqtype, e.info), a.rdLoc]))
-  gcUsage(e)
+  gcUsage(p.config, e)
 
 proc genConstExpr(p: BProc, n: PNode): Rope
 proc handleConstExpr(p: BProc, n: PNode, d: var TLoc): bool =
@@ -1226,7 +1230,7 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) =
       rawGenNew(p, tmp, nil)
       t = t.lastSon.skipTypes(abstractInst)
       r = "(*$1)" % [r]
-      gcUsage(e)
+      gcUsage(p.config, e)
     else:
       constructLoc(p, tmp)
   else:
@@ -1240,7 +1244,7 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) =
     tmp2.r = r
     let field = lookupFieldAgain(p, ty, it.sons[0].sym, tmp2.r)
     if field.loc.r == nil: fillObjectFields(p.module, ty)
-    if field.loc.r == nil: internalError(e.info, "genObjConstr")
+    if field.loc.r == nil: internalError(p.config, e.info, "genObjConstr")
     if it.len == 3 and optFieldCheck in p.options:
       genFieldCheck(p, it.sons[2], r, field)
     add(tmp2.r, ".")
@@ -1276,10 +1280,10 @@ proc genSeqConstr(p: BProc, n: PNode, d: var TLoc) =
   genNewSeqAux(p, dest[], intLiteral(sonsLen(n)))
   for i in countup(0, sonsLen(n) - 1):
     initLoc(arr, locExpr, n[i], OnHeap)
-    arr.r = rfmt(nil, "$1->data[$2]", rdLoc(dest[]), intLiteral(i))
+    arr.r = ropecg(p.module, "$1->data[$2]", rdLoc(dest[]), intLiteral(i))
     arr.storage = OnHeap            # we know that sequences are on the heap
     expr(p, n[i], arr)
-  gcUsage(n)
+  gcUsage(p.config, n)
   if doesAlias:
     if d.k == locNone:
       d = tmp
@@ -1302,21 +1306,21 @@ proc genArrToSeq(p: BProc, n: PNode, d: var TLoc) =
   if L < 10:
     for i in countup(0, L - 1):
       initLoc(elem, locExpr, lodeTyp elemType(skipTypes(n.typ, abstractInst)), OnHeap)
-      elem.r = rfmt(nil, "$1->data[$2]", rdLoc(d), intLiteral(i))
+      elem.r = ropecg(p.module, "$1->data[$2]", rdLoc(d), intLiteral(i))
       elem.storage = OnHeap # we know that sequences are on the heap
       initLoc(arr, locExpr, lodeTyp elemType(skipTypes(n.sons[1].typ, abstractInst)), a.storage)
-      arr.r = rfmt(nil, "$1[$2]", rdLoc(a), intLiteral(i))
+      arr.r = ropecg(p.module, "$1[$2]", rdLoc(a), intLiteral(i))
       genAssignment(p, elem, arr, {afDestIsNil, needToCopy})
   else:
     var i: TLoc
-    getTemp(p, getSysType(tyInt), i)
+    getTemp(p, getSysType(p.module.g.graph, unknownLineInfo(), tyInt), i)
     let oldCode = p.s(cpsStmts)
     linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n",  i.r, L.rope)
     initLoc(elem, locExpr, lodeTyp elemType(skipTypes(n.typ, abstractInst)), OnHeap)
-    elem.r = rfmt(nil, "$1->data[$2]", rdLoc(d), rdLoc(i))
+    elem.r = ropecg(p.module, "$1->data[$2]", rdLoc(d), rdLoc(i))
     elem.storage = OnHeap # we know that sequences are on the heap
     initLoc(arr, locExpr, lodeTyp elemType(skipTypes(n.sons[1].typ, abstractInst)), a.storage)
-    arr.r = rfmt(nil, "$1[$2]", rdLoc(a), rdLoc(i))
+    arr.r = ropecg(p.module, "$1[$2]", rdLoc(a), rdLoc(i))
     genAssignment(p, elem, arr, {afDestIsNil, needToCopy})
     lineF(p, cpsStmts, "}$n", [])
 
@@ -1338,7 +1342,7 @@ proc genNewFinalize(p: BProc, e: PNode) =
   genAssignment(p, a, b, {})  # set the object type:
   bt = skipTypes(refType.lastSon, abstractRange)
   genObjectInit(p, cpsStmts, bt, a, false)
-  gcUsage(e)
+  gcUsage(p.config, e)
 
 proc genOfHelper(p: BProc; dest: PType; a: Rope; info: TLineInfo): Rope =
   # unfortunately 'genTypeInfo' sets tfObjHasKids as a side effect, so we
@@ -1352,10 +1356,10 @@ proc genOfHelper(p: BProc; dest: PType; a: Rope; info: TLineInfo): Rope =
     inc p.module.labels
     let cache = "Nim_OfCheck_CACHE" & p.module.labels.rope
     addf(p.module.s[cfsVars], "static TNimType* $#[2];$n", [cache])
-    result = rfmt(p.module, "#isObjWithCache($#.m_type, $#, $#)", a, ti, cache)
+    result = ropecg(p.module, "#isObjWithCache($#.m_type, $#, $#)", a, ti, cache)
   when false:
     # former version:
-    result = rfmt(p.module, "#isObj($1.m_type, $2)",
+    result = ropecg(p.module, "#isObj($1.m_type, $2)",
                   a, genTypeInfo(p.module, dest, info))
 
 proc genOf(p: BProc, x: PNode, typ: PType, d: var TLoc) =
@@ -1368,7 +1372,7 @@ proc genOf(p: BProc, x: PNode, typ: PType, d: var TLoc) =
   while t.kind in {tyVar, tyLent, tyPtr, tyRef}:
     if t.kind notin {tyVar, tyLent}: nilCheck = r
     if t.kind notin {tyVar, tyLent} or not p.module.compileToCpp:
-      r = rfmt(nil, "(*$1)", r)
+      r = ropecg(p.module, "(*$1)", r)
     t = skipTypes(t.lastSon, typedescInst)
   discard getTypeDesc(p.module, t)
   if not p.module.compileToCpp:
@@ -1376,12 +1380,12 @@ proc genOf(p: BProc, x: PNode, typ: PType, d: var TLoc) =
       add(r, ~".Sup")
       t = skipTypes(t.sons[0], skipPtrs)
   if isObjLackingTypeField(t):
-    globalError(x.info, errGenerated,
+    globalError(p.config, x.info,
       "no 'of' operator available for pure objects")
   if nilCheck != nil:
-    r = rfmt(p.module, "(($1) && ($2))", nilCheck, genOfHelper(p, dest, r, x.info))
+    r = ropecg(p.module, "(($1) && ($2))", nilCheck, genOfHelper(p, dest, r, x.info))
   else:
-    r = rfmt(p.module, "($1)", genOfHelper(p, dest, r, x.info))
+    r = ropecg(p.module, "($1)", genOfHelper(p, dest, r, x.info))
   putIntoDest(p, d, x, r, a.storage)
 
 proc genOf(p: BProc, n: PNode, d: var TLoc) =
@@ -1417,11 +1421,11 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) =
       putIntoDest(p, b, e, "$1, $1Len_0" % [rdLoc(a)], a.storage)
     of tyString, tySequence:
       putIntoDest(p, b, e,
-                  "$1->data, $1->$2" % [rdLoc(a), lenField(p)], a.storage)
+                  "$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)
-    else: internalError(e.sons[0].info, "genRepr()")
+    else: internalError(p.config, e.sons[0].info, "genRepr()")
     putIntoDest(p, d, e,
         ropecg(p.module, "#reprOpenArray($1, $2)", [rdLoc(b),
         genTypeInfo(p.module, elemType(t), e.info)]), a.storage)
@@ -1430,12 +1434,12 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) =
                 ropecg(p.module, "#reprAny($1, $2)", [
                 rdLoc(a), genTypeInfo(p.module, t, e.info)]), a.storage)
   of tyEmpty, tyVoid:
-    localError(e.info, "'repr' doesn't support 'void' type")
+    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)]),
                                a.storage)
-  gcUsage(e)
+  gcUsage(p.config, e)
 
 proc genGetTypeInfo(p: BProc, e: PNode, d: var TLoc) =
   let t = e.sons[1].typ
@@ -1447,7 +1451,7 @@ proc genDollar(p: BProc, n: PNode, d: var TLoc, frmt: string) =
   a.r = ropecg(p.module, frmt, [rdLoc(a)])
   if d.k == locNone: getTemp(p, n.typ, d)
   genAssignment(p, d, a, {})
-  gcUsage(n)
+  gcUsage(p.config, n)
 
 proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
   var a = e.sons[1]
@@ -1489,7 +1493,7 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
     # 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)))
-  else: internalError(e.info, "genArrayLen()")
+  else: internalError(p.config, e.info, "genArrayLen()")
 
 proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) =
   var a, b: TLoc
@@ -1500,18 +1504,18 @@ proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) =
   initLocExpr(p, e.sons[2], b)
   let t = skipTypes(e.sons[1].typ, {tyVar})
   let setLenPattern = if not p.module.compileToCpp:
-      "$1 = ($3) #setLengthSeq(&($1)->Sup, sizeof($4), $2);$n"
+      "$1 = ($3) #setLengthSeqV2(&($1)->Sup, $4, $2);$n"
     else:
-      "$1 = ($3) #setLengthSeq($1, sizeof($4), $2);$n"
+      "$1 = ($3) #setLengthSeqV2($1, $4, $2);$n"
 
   lineCg(p, cpsStmts, setLenPattern, [
       rdLoc(a), rdLoc(b), getTypeDesc(p.module, t),
-      getTypeDesc(p.module, t.skipTypes(abstractInst).sons[0])])
-  gcUsage(e)
+      genTypeInfo(p.module, t.skipTypes(abstractInst), e.info)])
+  gcUsage(p.config, e)
 
 proc genSetLengthStr(p: BProc, e: PNode, d: var TLoc) =
   binaryStmt(p, e, d, "$1 = #setLengthStr($1, $2);$n")
-  gcUsage(e)
+  gcUsage(p.config, e)
 
 proc genSwap(p: BProc, e: PNode, d: var TLoc) =
   # swap(a, b) -->
@@ -1537,7 +1541,7 @@ proc rdSetElemLoc(a: TLoc, setType: PType): Rope =
 proc fewCmps(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: internalError(s.info, "fewCmps")
+  if s.kind != nkCurly: return false
   if (getSize(s.typ) <= platform.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}:
@@ -1633,17 +1637,17 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
     of mSymDiffSet: binaryExpr(p, e, d, "($1 ^ $2)")
     of mInSet:
       genInOp(p, e, d)
-    else: internalError(e.info, "genSetOp()")
+    else: internalError(p.config, e.info, "genSetOp()")
   else:
     case op
     of mIncl: binaryStmtInExcl(p, e, d, "$1[(NU)($2)>>3] |=(1U<<($2&7U));$n")
     of mExcl: binaryStmtInExcl(p, e, d, "$1[(NU)($2)>>3] &= ~(1U<<($2&7U));$n")
     of mCard: unaryExprChar(p, e, d, "#cardSet($1, " & $size & ')')
     of mLtSet, mLeSet:
-      getTemp(p, getSysType(tyInt), i) # our counter
+      getTemp(p, getSysType(p.module.g.graph, unknownLineInfo(), tyInt), i) # our counter
       initLocExpr(p, e.sons[1], a)
       initLocExpr(p, e.sons[2], b)
-      if d.k == locNone: getTemp(p, getSysType(tyBool), d)
+      if d.k == locNone: getTemp(p, getSysType(p.module.g.graph, unknownLineInfo(), tyBool), d)
       lineF(p, cpsStmts, lookupOpr[op],
            [rdLoc(i), rope(size), rdLoc(d), rdLoc(a), rdLoc(b)])
     of mEqSet:
@@ -1651,7 +1655,7 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
       binaryExprChar(p, e, d, "(memcmp($1, $2, " & $(size) & ")==0)")
     of mMulSet, mPlusSet, mMinusSet, mSymDiffSet:
       # we inline the simple for loop for better code generation:
-      getTemp(p, getSysType(tyInt), i) # our counter
+      getTemp(p, getSysType(p.module.g.graph, unknownLineInfo(), tyInt), i) # our counter
       initLocExpr(p, e.sons[1], a)
       initLocExpr(p, e.sons[2], b)
       if d.k == locNone: getTemp(p, a.t, d)
@@ -1661,7 +1665,7 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
           rdLoc(i), rope(size), rdLoc(d), rdLoc(a), rdLoc(b),
           rope(lookupOpr[op])])
     of mInSet: genInOp(p, e, d)
-    else: internalError(e.info, "genSetOp")
+    else: internalError(p.config, e.info, "genSetOp")
 
 proc genOrd(p: BProc, e: PNode, d: var TLoc) =
   unaryExprChar(p, e, d, "$1")
@@ -1740,7 +1744,7 @@ proc genConv(p: BProc, e: PNode, d: var TLoc) =
 proc convStrToCStr(p: BProc, n: PNode, d: var TLoc) =
   var a: TLoc
   initLocExpr(p, n.sons[0], a)
-  putIntoDest(p, d, n, "$1->data" % [rdLoc(a)],
+  putIntoDest(p, d, n, "($1 ? $1->data : (NCSTRING)\"\")" % [rdLoc(a)],
               a.storage)
 
 proc convCStrToStr(p: BProc, n: PNode, d: var TLoc) =
@@ -1749,22 +1753,20 @@ proc convCStrToStr(p: BProc, n: PNode, d: var TLoc) =
   putIntoDest(p, d, n,
               ropecg(p.module, "#cstrToNimstr($1)", [rdLoc(a)]),
               a.storage)
-  gcUsage(n)
+  gcUsage(p.config, n)
 
 proc genStrEquals(p: BProc, e: PNode, d: var TLoc) =
   var x: TLoc
   var a = e.sons[1]
   var b = e.sons[2]
-  if (a.kind == nkNilLit) or (b.kind == nkNilLit):
-    binaryExpr(p, e, d, "($1 == $2)")
-  elif (a.kind in {nkStrLit..nkTripleStrLit}) and (a.strVal == ""):
+  if a.kind in {nkStrLit..nkTripleStrLit} and a.strVal == "":
     initLocExpr(p, e.sons[2], x)
     putIntoDest(p, d, e,
-      rfmt(nil, "(($1) && ($1)->$2 == 0)", rdLoc(x), lenField(p)))
-  elif (b.kind in {nkStrLit..nkTripleStrLit}) and (b.strVal == ""):
+      ropecg(p.module, "(!($1) || ($1)->$2 == 0)", rdLoc(x), lenField(p)))
+  elif b.kind in {nkStrLit..nkTripleStrLit} and b.strVal == "":
     initLocExpr(p, e.sons[1], x)
     putIntoDest(p, d, e,
-      rfmt(nil, "(($1) && ($1)->$2 == 0)", rdLoc(x), lenField(p)))
+      ropecg(p.module, "(!($1) || ($1)->$2 == 0)", rdLoc(x), lenField(p)))
   else:
     binaryExpr(p, e, d, "#eqStrings($1, $2)")
 
@@ -1776,7 +1778,7 @@ proc binaryFloatArith(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
     assert(e.sons[2].typ != nil)
     initLocExpr(p, e.sons[1], a)
     initLocExpr(p, e.sons[2], b)
-    putIntoDest(p, d, e, rfmt(nil, "(($4)($2) $1 ($4)($3))",
+    putIntoDest(p, d, e, ropecg(p.module, "(($4)($2) $1 ($4)($3))",
                               rope(opr[m]), rdLoc(a), rdLoc(b),
                               getSimpleTypeDesc(p.module, e[1].typ)))
     if optNaNCheck in p.options:
@@ -1885,12 +1887,12 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
   of mEcho: genEcho(p, e[1].skipConv)
   of mArrToSeq: genArrToSeq(p, e, d)
   of mNLen..mNError, mSlurp..mQuoteAst:
-    localError(e.info, errXMustBeCompileTime, e.sons[0].sym.name.s)
+    localError(p.config, e.info, strutils.`%`(errXMustBeCompileTime, e.sons[0].sym.name.s))
   of mSpawn:
-    let n = lowerings.wrapProcForSpawn(p.module.module, e, e.typ, nil, nil)
+    let n = lowerings.wrapProcForSpawn(p.module.g.graph, p.module.module, e, e.typ, nil, nil)
     expr(p, n, d)
   of mParallel:
-    let n = semparallel.liftParallel(p.module.module, e)
+    let n = semparallel.liftParallel(p.module.g.graph, p.module.module, e)
     expr(p, n, d)
   of mDeepCopy:
     var a, b: TLoc
@@ -1902,7 +1904,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
   else:
     when defined(debugMagics):
       echo p.prc.name.s, " ", p.prc.id, " ", p.prc.flags, " ", p.prc.ast[genericParamsPos].kind
-    internalError(e.info, "genMagicExpr: " & $op)
+    internalError(p.config, e.info, "genMagicExpr: " & $op)
 
 proc genSetConstr(p: BProc, e: PNode, d: var TLoc) =
   # example: { a..b, c, d, e, f..g }
@@ -1922,7 +1924,7 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) =
           [rdLoc(d), getTypeDesc(p.module, e.typ)])
       for it in e.sons:
         if it.kind == nkRange:
-          getTemp(p, getSysType(tyInt), idx) # our counter
+          getTemp(p, getSysType(p.module.g.graph, unknownLineInfo(), tyInt), idx) # our counter
           initLocExpr(p, it.sons[0], a)
           initLocExpr(p, it.sons[1], b)
           lineF(p, cpsStmts, "for ($1 = $3; $1 <= $4; $1++) $n" &
@@ -1938,7 +1940,7 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) =
       lineF(p, cpsStmts, "$1 = 0;$n", [rdLoc(d)])
       for it in e.sons:
         if it.kind == nkRange:
-          getTemp(p, getSysType(tyInt), idx) # our counter
+          getTemp(p, getSysType(p.module.g.graph, unknownLineInfo(), tyInt), idx) # our counter
           initLocExpr(p, it.sons[0], a)
           initLocExpr(p, it.sons[1], b)
           lineF(p, cpsStmts, "for ($1 = $3; $1 <= $4; $1++) $n" &
@@ -1982,7 +1984,7 @@ proc genClosure(p: BProc, n: PNode, d: var TLoc) =
     initLocExpr(p, n.sons[0], a)
     initLocExpr(p, n.sons[1], b)
     if n.sons[0].skipConv.kind == nkClosure:
-      internalError(n.info, "closure to closure created")
+      internalError(p.config, n.info, "closure to closure created")
     # tasyncawait.nim breaks with this optimization:
     when false:
       if d.k != locNone:
@@ -2023,7 +2025,7 @@ template genStmtListExprImpl(exprOrStmt) {.dirty.} =
         let theMacro = it[0].sym
         add p.s(cpsStmts), initFrameNoDebug(p, frameName,
            makeCString theMacro.name.s,
-           theMacro.info.quotedFilename, it.info.line)
+           quotedFilename(p.config, theMacro.info), it.info.line.int)
     else:
       genStmts(p, it)
   if n.len > 0: exprOrStmt
@@ -2144,11 +2146,11 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
       #if sym.kind == skIterator:
       #  echo renderTree(sym.getBody, {renderIds})
       if sfCompileTime in sym.flags:
-        localError(n.info, "request to generate code for .compileTime proc: " &
+        localError(p.config, n.info, "request to generate code for .compileTime proc: " &
            sym.name.s)
       genProc(p.module, sym)
       if sym.loc.r == nil or sym.loc.lode == nil:
-        internalError(n.info, "expr: proc not init " & sym.name.s)
+        internalError(p.config, n.info, "expr: proc not init " & sym.name.s)
       putLocIntoDest(p, d, sym.loc)
     of skConst:
       if isSimpleConst(sym.typ):
@@ -2166,10 +2168,10 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
       if sym.loc.r == nil or sym.loc.t == nil:
         #echo "FAILED FOR PRCO ", p.prc.name.s
         #echo renderTree(p.prc.ast, {renderIds})
-        internalError n.info, "expr: var not init " & sym.name.s & "_" & $sym.id
+        internalError p.config, n.info, "expr: var not init " & sym.name.s & "_" & $sym.id
       if sfThread in sym.flags:
         accessThreadLocalVar(p, sym)
-        if emulatedThreadVars():
+        if emulatedThreadVars(p.config):
           putIntoDest(p, d, sym.loc.lode, "NimTV_->" & sym.loc.r)
         else:
           putLocIntoDest(p, d, sym.loc)
@@ -2179,16 +2181,16 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
       if sym.loc.r == nil or sym.loc.t == nil:
         #echo "FAILED FOR PRCO ", p.prc.name.s
         #echo renderTree(p.prc.ast, {renderIds})
-        internalError(n.info, "expr: temp not init " & sym.name.s & "_" & $sym.id)
+        internalError(p.config, n.info, "expr: temp not init " & sym.name.s & "_" & $sym.id)
       putLocIntoDest(p, d, sym.loc)
     of skParam:
       if sym.loc.r == nil or sym.loc.t == nil:
         # echo "FAILED FOR PRCO ", p.prc.name.s
         # debug p.prc.typ.n
         # echo renderTree(p.prc.ast, {renderIds})
-        internalError(n.info, "expr: param not init " & sym.name.s & "_" & $sym.id)
+        internalError(p.config, n.info, "expr: param not init " & sym.name.s & "_" & $sym.id)
       putLocIntoDest(p, d, sym.loc)
-    else: internalError(n.info, "expr(" & $sym.kind & "); unknown symbol")
+    else: internalError(p.config, n.info, "expr(" & $sym.kind & "); unknown symbol")
   of nkNilLit:
     if not isEmptyType(n.typ):
       putIntoDest(p, d, n, genLiteral(p, n))
@@ -2257,15 +2259,15 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
     var sym = n.sons[namePos].sym
     genProc(p.module, sym)
     if sym.loc.r == nil or sym.loc.lode == nil:
-      internalError(n.info, "expr: proc not init " & sym.name.s)
+      internalError(p.config, n.info, "expr: proc not init " & sym.name.s)
     putLocIntoDest(p, d, sym.loc)
   of nkClosure: genClosure(p, n, d)
 
   of nkEmpty: discard
   of nkWhileStmt: genWhileStmt(p, n)
   of nkVarSection, nkLetSection: genVarStmt(p, n)
-  of nkConstSection: genConstStmt(p, n)
-  of nkForStmt: internalError(n.info, "for statement not eliminated")
+  of nkConstSection: discard  # consts generated lazily on use
+  of nkForStmt: internalError(p.config, n.info, "for statement not eliminated")
   of nkCaseStmt: genCase(p, n, d)
   of nkReturnStmt: genReturnStmt(p, n)
   of nkBreakStmt: genBreakStmt(p, n)
@@ -2293,7 +2295,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
         initLocExpr(p, ex, a)
   of nkAsmStmt: genAsmStmt(p, n)
   of nkTryStmt:
-    if p.module.compileToCpp and optNoCppExceptions notin gGlobalOptions:
+    if p.module.compileToCpp and optNoCppExceptions notin p.config.globalOptions:
       genTryCpp(p, n, d)
     else:
       genTry(p, n, d)
@@ -2315,8 +2317,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
       # are not transformed correctly. We work around this issue (#411) here
       # by ensuring it's no inner proc (owner is a module):
       if prc.skipGenericOwner.kind == skModule and sfCompileTime notin prc.flags:
-        if (not emitLazily(prc)) or
-            ({sfExportc, sfCompilerProc} * prc.flags == {sfExportc}) or
+        if ({sfExportc, sfCompilerProc} * prc.flags == {sfExportc}) or
             (sfExportc in prc.flags and lfExportLib in prc.loc.flags) or
             (prc.kind == skMethod):
           # we have not only the header:
@@ -2326,7 +2327,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
   of nkState: genState(p, n)
   of nkGotoState: genGotoState(p, n)
   of nkBreakState: genBreakState(p, n)
-  else: internalError(n.info, "expr(" & $n.kind & "); unknown node kind")
+  else: internalError(p.config, n.info, "expr(" & $n.kind & "); unknown node kind")
 
 proc genNamedConstExpr(p: BProc, n: PNode): Rope =
   if n.kind == nkExprColonExpr: result = genConstExpr(p, n.sons[1])
@@ -2362,7 +2363,7 @@ proc getDefaultValue(p: BProc; typ: PType; info: TLineInfo): Rope =
     if mapType(t) == ctArray: result = rope"{}"
     else: result = rope"0"
   else:
-    globalError(info, "cannot create null element for: " & $t.kind)
+    globalError(p.config, info, "cannot create null element for: " & $t.kind)
 
 proc getNullValueAux(p: BProc; t: PType; obj, cons: PNode, result: var Rope; count: var int) =
   case obj.kind
@@ -2388,7 +2389,7 @@ proc getNullValueAux(p: BProc; t: PType; obj, cons: PNode, result: var Rope; cou
     # not found, produce default value:
     result.add getDefaultValue(p, field.typ, cons.info)
   else:
-    localError(cons.info, "cannot create null element for: " & $obj)
+    localError(p.config, cons.info, "cannot create null element for: " & $obj)
 
 proc getNullValueAuxT(p: BProc; orig, t: PType; obj, cons: PNode, result: var Rope; count: var int) =
   var base = t.sons[0]
diff --git a/compiler/ccgliterals.nim b/compiler/ccgliterals.nim
index b17a5eedd..cfe71375e 100644
--- a/compiler/ccgliterals.nim
+++ b/compiler/ccgliterals.nim
@@ -15,7 +15,7 @@
 
 template detectVersion(field, corename) =
   if m.g.field == 0:
-    let core = getCompilerProc(corename)
+    let core = getCompilerProc(m.g.graph, corename)
     if core == nil or core.kind != skConst:
       m.g.field = 1
     else:
@@ -74,7 +74,7 @@ proc genStringLiteralDataOnly(m: BModule; s: string; info: TLineInfo): Rope =
   of 0, 1: result = genStringLiteralDataOnlyV1(m, s)
   of 2: result = genStringLiteralDataOnlyV2(m, s)
   else:
-    localError(info, "cannot determine how to produce code for string literal")
+    localError(m.config, info, "cannot determine how to produce code for string literal")
 
 proc genStringLiteralFromData(m: BModule; data: Rope; info: TLineInfo): Rope =
   result = ropecg(m, "((#NimStringDesc*) &$1)",
@@ -88,4 +88,4 @@ proc genStringLiteral(m: BModule; n: PNode): Rope =
   of 0, 1: result = genStringLiteralV1(m, n)
   of 2: result = genStringLiteralV2(m, n)
   else:
-    localError(n.info, "cannot determine how to produce code for string literal")
+    localError(m.config, n.info, "cannot determine how to produce code for string literal")
diff --git a/compiler/ccgmerge.nim b/compiler/ccgmerge.nim
index f667be70f..4b4f9c0e6 100644
--- a/compiler/ccgmerge.nim
+++ b/compiler/ccgmerge.nim
@@ -45,28 +45,28 @@ const
   ]
   NimMergeEndMark = "/*\tNIM_merge_END:*/"
 
-proc genSectionStart*(fs: TCFileSection): Rope =
-  if compilationCachePresent:
+proc genSectionStart*(fs: TCFileSection; conf: ConfigRef): Rope =
+  if compilationCachePresent(conf):
     result = rope(tnl)
     add(result, "/*\t")
     add(result, CFileSectionNames[fs])
     add(result, ":*/")
     add(result, tnl)
 
-proc genSectionEnd*(fs: TCFileSection): Rope =
-  if compilationCachePresent:
+proc genSectionEnd*(fs: TCFileSection; conf: ConfigRef): Rope =
+  if compilationCachePresent(conf):
     result = rope(NimMergeEndMark & tnl)
 
-proc genSectionStart*(ps: TCProcSection): Rope =
-  if compilationCachePresent:
+proc genSectionStart*(ps: TCProcSection; conf: ConfigRef): Rope =
+  if compilationCachePresent(conf):
     result = rope(tnl)
     add(result, "/*\t")
     add(result, CProcSectionNames[ps])
     add(result, ":*/")
     add(result, tnl)
 
-proc genSectionEnd*(ps: TCProcSection): Rope =
-  if compilationCachePresent:
+proc genSectionEnd*(ps: TCProcSection; conf: ConfigRef): Rope =
+  if compilationCachePresent(conf):
     result = rope(NimMergeEndMark & tnl)
 
 proc writeTypeCache(a: TypeCache, s: var string) =
@@ -96,7 +96,7 @@ proc writeIntSet(a: IntSet, s: var string) =
   s.add('}')
 
 proc genMergeInfo*(m: BModule): Rope =
-  if not compilationCachePresent: return nil
+  if not compilationCachePresent(m.config): return nil
   var s = "/*\tNIM_merge_INFO:"
   s.add(tnl)
   s.add("typeCache:{")
@@ -161,7 +161,7 @@ proc readVerbatimSection(L: var TBaseLexer): Rope =
       buf = L.buf
       r.add(tnl)
     of '\0':
-      internalError("ccgmerge: expected: " & NimMergeEndMark)
+      doAssert(false, "ccgmerge: expected: " & NimMergeEndMark)
       break
     else:
       if atEndMark(buf, pos):
@@ -179,7 +179,7 @@ proc readKey(L: var TBaseLexer, result: var string) =
   while buf[pos] in IdentChars:
     result.add(buf[pos])
     inc pos
-  if buf[pos] != ':': internalError("ccgmerge: ':' expected")
+  if buf[pos] != ':': doAssert(false, "ccgmerge: ':' expected")
   L.bufpos = pos + 1 # skip ':'
 
 proc newFakeType(id: int): PType =
@@ -187,12 +187,12 @@ proc newFakeType(id: int): PType =
   result.id = id
 
 proc readTypeCache(L: var TBaseLexer, result: var TypeCache) =
-  if ^L.bufpos != '{': internalError("ccgmerge: '{' expected")
+  if ^L.bufpos != '{': doAssert(false, "ccgmerge: '{' expected")
   inc L.bufpos
   while ^L.bufpos != '}':
     skipWhite(L)
     var key = decodeStr(L.buf, L.bufpos)
-    if ^L.bufpos != ':': internalError("ccgmerge: ':' expected")
+    if ^L.bufpos != ':': doAssert(false, "ccgmerge: ':' expected")
     inc L.bufpos
     var value = decodeStr(L.buf, L.bufpos)
     # XXX implement me
@@ -201,7 +201,7 @@ proc readTypeCache(L: var TBaseLexer, result: var TypeCache) =
   inc L.bufpos
 
 proc readIntSet(L: var TBaseLexer, result: var IntSet) =
-  if ^L.bufpos != '{': internalError("ccgmerge: '{' expected")
+  if ^L.bufpos != '{': doAssert(false, "ccgmerge: '{' expected")
   inc L.bufpos
   while ^L.bufpos != '}':
     skipWhite(L)
@@ -225,7 +225,7 @@ proc processMergeInfo(L: var TBaseLexer, m: BModule) =
     of "labels":    m.labels = decodeVInt(L.buf, L.bufpos)
     of "flags":
       m.flags = cast[set[CodegenFlag]](decodeVInt(L.buf, L.bufpos) != 0)
-    else: internalError("ccgmerge: unknown key: " & k)
+    else: doAssert(false, "ccgmerge: unknown key: " & k)
 
 when not defined(nimhygiene):
   {.pragma: inject.}
@@ -275,9 +275,9 @@ proc readMergeSections(cfilename: string, m: var TMergeSections) =
         if sectionB >= 0 and sectionB <= high(TCProcSection).int:
           m.p[TCProcSection(sectionB)] = verbatim
         else:
-          internalError("ccgmerge: unknown section: " & k)
+          doAssert(false, "ccgmerge: unknown section: " & k)
     else:
-      internalError("ccgmerge: '*/' expected")
+      doAssert(false, "ccgmerge: '*/' expected")
 
 proc mergeRequired*(m: BModule): bool =
   for i in cfsHeaders..cfsProcs:
diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim
index 2030d6add..1e0a3c818 100644
--- a/compiler/ccgstmts.nim
+++ b/compiler/ccgstmts.nim
@@ -16,7 +16,7 @@ const
     # above X strings a hash-switch for strings is generated
 
 proc registerGcRoot(p: BProc, v: PSym) =
-  if gSelectedGC in {gcMarkAndSweep, gcGenerational, gcV2, gcRefc} and
+  if p.config.selectedGC in {gcMarkAndSweep, gcGenerational, gcV2, gcRefc} and
       containsGarbageCollectedRef(v.loc.t):
     # we register a specialized marked proc here; this has the advantage
     # that it works out of the box for thread local storage then :-)
@@ -43,13 +43,13 @@ proc inExceptBlockLen(p: BProc): int =
 
 proc genVarTuple(p: BProc, n: PNode) =
   var tup, field: TLoc
-  if n.kind != nkVarTuple: internalError(n.info, "genVarTuple")
+  if n.kind != nkVarTuple: internalError(p.config, n.info, "genVarTuple")
   var L = sonsLen(n)
 
   # if we have a something that's been captured, use the lowering instead:
   for i in countup(0, L-3):
     if n[i].kind != nkSym:
-      genStmts(p, lowerTupleUnpacking(n, p.prc))
+      genStmts(p, lowerTupleUnpacking(p.module.g.graph, n, p.prc))
       return
 
   genLineDir(p, n)
@@ -70,7 +70,7 @@ proc genVarTuple(p: BProc, n: PNode) =
     if t.kind == tyTuple:
       field.r = "$1.Field$2" % [rdLoc(tup), rope(i)]
     else:
-      if t.n.sons[i].kind != nkSym: internalError(n.info, "genVarTuple")
+      if t.n.sons[i].kind != nkSym: internalError(p.config, n.info, "genVarTuple")
       field.r = "$1.$2" % [rdLoc(tup), mangleRecFieldName(p.module, t.n.sons[i].sym, t)]
     putLocIntoDest(p, v.loc, field)
 
@@ -125,7 +125,7 @@ proc endBlock(p: BProc, blockEnd: Rope) =
 proc endBlock(p: BProc) =
   let topBlock = p.blocks.len - 1
   var blockEnd = if p.blocks[topBlock].label != nil:
-      rfmt(nil, "} $1: ;$n", p.blocks[topBlock].label)
+      ropecg(p.module, "} $1: ;$n", p.blocks[topBlock].label)
     else:
       ~"}$n"
   let frameLen = p.blocks[topBlock].frameLen
@@ -149,7 +149,7 @@ template preserveBreakIdx(body: untyped): untyped =
   p.breakIdx = oldBreakIdx
 
 proc genState(p: BProc, n: PNode) =
-  internalAssert n.len == 1
+  internalAssert p.config, n.len == 1
   let n0 = n[0]
   if n0.kind == nkIntLit:
     let idx = n.sons[0].intVal
@@ -191,7 +191,7 @@ proc genBreakState(p: BProc, n: PNode) =
 
 proc genGotoVar(p: BProc; value: PNode) =
   if value.kind notin {nkCharLit..nkUInt64Lit}:
-    localError(value.info, "'goto' target must be a literal value")
+    localError(p.config, value.info, "'goto' target must be a literal value")
   else:
     lineF(p, cpsStmts, "goto NIMSTATE_$#;$n", [value.intVal.rope])
 
@@ -280,20 +280,6 @@ proc genVarStmt(p: BProc, n: PNode) =
     else:
       genVarTuple(p, it)
 
-proc genConstStmt(p: BProc, n: PNode) =
-  for it in n.sons:
-    if it.kind == nkCommentStmt: continue
-    if it.kind != nkConstDef: internalError(n.info, "genConstStmt")
-
-    let sym = it.sons[0].sym
-    if sym.typ.containsCompileTimeOnly or
-        sym.typ.kind notin ConstantDataTypes or
-        sym.ast.len == 0 or
-        emitLazily(sym):
-      continue
-
-    requestConstImpl(p, sym)
-
 proc genIf(p: BProc, n: PNode, d: var TLoc) =
   #
   #  { if (!expr1) goto L1;
@@ -339,7 +325,7 @@ proc genIf(p: BProc, n: PNode, d: var TLoc) =
       startBlock(p)
       expr(p, it.sons[0], d)
       endBlock(p)
-    else: internalError(n.info, "genIf()")
+    else: internalError(p.config, n.info, "genIf()")
   if sonsLen(n) > 1: fixLabel(p, lend)
 
 
@@ -351,7 +337,7 @@ proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) =
 
   for i in countup(1, howManyTrys):
     let tryStmt = p.nestedTryStmts.pop
-    if not p.module.compileToCpp or optNoCppExceptions in gGlobalOptions:
+    if not p.module.compileToCpp or optNoCppExceptions in p.config.globalOptions:
       # Pop safe points generated by try
       if not tryStmt.inExcept:
         linefmt(p, cpsStmts, "#popSafePoint();$n")
@@ -370,7 +356,7 @@ proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) =
   for i in countdown(howManyTrys-1, 0):
     p.nestedTryStmts.add(stack[i])
 
-  if not p.module.compileToCpp or optNoCppExceptions in gGlobalOptions:
+  if not p.module.compileToCpp or optNoCppExceptions in p.config.globalOptions:
     # Pop exceptions that was handled by the
     # except-blocks we are in
     for i in countdown(howManyExcepts-1, 0):
@@ -397,7 +383,7 @@ proc genGotoForCase(p: BProc; caseStmt: PNode) =
     let it = caseStmt.sons[i]
     for j in 0 .. it.len-2:
       if it.sons[j].kind == nkRange:
-        localError(it.info, "range notation not available for computed goto")
+        localError(p.config, it.info, "range notation not available for computed goto")
         return
       let val = getOrdValue(it.sons[j])
       lineF(p, cpsStmts, "NIMSTATE_$#:$n", [val.rope])
@@ -412,19 +398,19 @@ proc genComputedGoto(p: BProc; n: PNode) =
     let it = n.sons[i]
     if it.kind == nkCaseStmt:
       if lastSon(it).kind != nkOfBranch:
-        localError(it.info,
+        localError(p.config, it.info,
             "case statement must be exhaustive for computed goto"); return
       casePos = i
       let aSize = lengthOrd(it.sons[0].typ)
       if aSize > 10_000:
-        localError(it.info,
+        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:
-        localError(it.info,
+        localError(p.config, it.info,
             "case statement has to start at 0 for computed goto"); return
   if casePos < 0:
-    localError(n.info, "no case statement found for computed goto"); return
+    localError(p.config, n.info, "no case statement found for computed goto"); return
   var id = p.labels+1
   inc p.labels, arraySize+1
   let tmp = "TMP$1_" % [id.rope]
@@ -458,7 +444,7 @@ proc genComputedGoto(p: BProc; n: PNode) =
     let it = caseStmt.sons[i]
     for j in 0 .. it.len-2:
       if it.sons[j].kind == nkRange:
-        localError(it.info, "range notation not available for computed goto")
+        localError(p.config, it.info, "range notation not available for computed goto")
         return
       let val = getOrdValue(it.sons[j])
       lineF(p, cpsStmts, "TMP$#_:$n", [intLiteral(val+id+1)])
@@ -562,7 +548,7 @@ proc genBreakStmt(p: BProc, t: PNode) =
     # an unnamed 'break' can only break a loop after 'transf' pass:
     while idx >= 0 and not p.blocks[idx].isLoop: dec idx
     if idx < 0 or not p.blocks[idx].isLoop:
-      internalError(t.info, "no loop to break")
+      internalError(p.config, t.info, "no loop to break")
   let label = assignLabel(p.blocks[idx])
   blockLeaveActions(p,
     p.nestedTryStmts.len - p.blocks[idx].nestedTryStmts,
@@ -585,15 +571,15 @@ proc genRaiseStmt(p: BProc, t: PNode) =
     var e = rdLoc(a)
     var typ = skipTypes(t[0].typ, abstractPtrs)
     genLineDir(p, t)
-    if isImportedException(typ):
+    if isImportedException(typ, p.config):
       lineF(p, cpsStmts, "throw $1;$n", [e])
-    else:      
+    else:
       lineCg(p, cpsStmts, "#raiseException((#Exception*)$1, $2);$n",
           [e, makeCString(typ.sym.name.s)])
   else:
     genLineDir(p, t)
     # reraise the last exception:
-    if p.module.compileToCpp and optNoCppExceptions notin gGlobalOptions:
+    if p.module.compileToCpp and optNoCppExceptions notin p.config.globalOptions:
       line(p, cpsStmts, ~"throw;$n")
     else:
       linefmt(p, cpsStmts, "#reraiseException();$n")
@@ -836,7 +822,7 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
     else:
       for j in 0..t[i].len-2:
         if t[i][j].isInfixAs():
-          let exvar = t[i][j][2] # ex1 in `except ExceptType as ex1:` 
+          let exvar = t[i][j][2] # ex1 in `except ExceptType as ex1:`
           fillLoc(exvar.sym.loc, locTemp, exvar, mangleLocalName(p, exvar.sym), OnUnknown)
           startBlock(p, "catch ($1& $2) {$n", getTypeDesc(p.module, t[i][j][1].typ), rdLoc(exvar.sym.loc))
         else:
@@ -890,17 +876,14 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) =
   p.module.includeHeader("<setjmp.h>")
   genLineDir(p, t)
   var safePoint = getTempName(p.module)
-  if getCompilerProc("Exception") != nil:
-    discard cgsym(p.module, "Exception")
-  else:
-    discard cgsym(p.module, "E_Base")
+  discard cgsym(p.module, "Exception")
   linefmt(p, cpsLocals, "#TSafePoint $1;$n", safePoint)
   linefmt(p, cpsStmts, "#pushSafePoint(&$1);$n", safePoint)
-  if isDefined("nimStdSetjmp"):
+  if isDefined(p.config, "nimStdSetjmp"):
     linefmt(p, cpsStmts, "$1.status = setjmp($1.context);$n", safePoint)
-  elif isDefined("nimSigSetjmp"):
+  elif isDefined(p.config, "nimSigSetjmp"):
     linefmt(p, cpsStmts, "$1.status = sigsetjmp($1.context, 0);$n", safePoint)
-  elif isDefined("nimRawSetjmp"):
+  elif isDefined(p.config, "nimRawSetjmp"):
     linefmt(p, cpsStmts, "$1.status = _setjmp($1.context);$n", safePoint)
   else:
     linefmt(p, cpsStmts, "$1.status = setjmp($1.context);$n", safePoint)
@@ -1027,7 +1010,7 @@ proc genEmit(p: BProc, t: PNode) =
   if p.prc == nil:
     # top level emit pragma?
     let section = determineSection(t[1])
-    genCLineDir(p.module.s[section], t.info)
+    genCLineDir(p.module.s[section], t.info, p.config)
     add(p.module.s[section], s)
   else:
     genLineDir(p, t)
@@ -1153,4 +1136,4 @@ proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) =
 proc genStmts(p: BProc, t: PNode) =
   var a: TLoc
   expr(p, t, a)
-  internalAssert a.k in {locNone, locTemp, locLocalVar}
+  internalAssert p.config, a.k in {locNone, locTemp, locLocalVar}
diff --git a/compiler/ccgthreadvars.nim b/compiler/ccgthreadvars.nim
index 505b69eab..da5c624b7 100644
--- a/compiler/ccgthreadvars.nim
+++ b/compiler/ccgthreadvars.nim
@@ -12,11 +12,11 @@
 
 # included from cgen.nim
 
-proc emulatedThreadVars(): bool =
-  result = {optThreads, optTlsEmulation} <= gGlobalOptions
+proc emulatedThreadVars(conf: ConfigRef): bool =
+  result = {optThreads, optTlsEmulation} <= conf.globalOptions
 
 proc accessThreadLocalVar(p: BProc, s: PSym) =
-  if emulatedThreadVars() and not p.threadVarAccessed:
+  if emulatedThreadVars(p.config) and not p.threadVarAccessed:
     p.threadVarAccessed = true
     incl p.module.flags, usesThreadVars
     addf(p.procSec(cpsLocals), "\tNimThreadVars* NimTV_;$n", [])
@@ -37,7 +37,7 @@ var
 # made to be one.
 
 proc declareThreadVar(m: BModule, s: PSym, isExtern: bool) =
-  if emulatedThreadVars():
+  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 :-(
@@ -46,7 +46,7 @@ proc declareThreadVar(m: BModule, s: PSym, isExtern: bool) =
       addf(nimtv, "$1 $2;$n", [getTypeDesc(m, s.loc.t), s.loc.r])
   else:
     if isExtern: add(m.s[cfsVars], "extern ")
-    if optThreads in gGlobalOptions: add(m.s[cfsVars], "NIM_THREADVAR ")
+    if optThreads in m.config.globalOptions: add(m.s[cfsVars], "NIM_THREADVAR ")
     add(m.s[cfsVars], getTypeDesc(m, s.loc.t))
     addf(m.s[cfsVars], " $1;$n", [s.loc.r])
 
@@ -57,7 +57,7 @@ proc generateThreadLocalStorage(m: BModule) =
 
 proc generateThreadVarsSize(m: BModule) =
   if nimtv != nil:
-    let externc = if gCmd == cmdCompileToCpp or
+    let externc = if m.config.cmd == cmdCompileToCpp or
                        sfCompileToCpp in m.module.flags: "extern \"C\" "
                   else: ""
     addf(m.s[cfsProcs],
diff --git a/compiler/ccgtrav.nim b/compiler/ccgtrav.nim
index 1bb2ba98d..c265064a1 100644
--- a/compiler/ccgtrav.nim
+++ b/compiler/ccgtrav.nim
@@ -29,12 +29,12 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, n: PNode;
     for i in countup(0, sonsLen(n) - 1):
       genTraverseProc(c, accessor, n.sons[i], typ)
   of nkRecCase:
-    if (n.sons[0].kind != nkSym): internalError(n.info, "genTraverseProc")
+    if (n.sons[0].kind != nkSym): internalError(c.p.config, n.info, "genTraverseProc")
     var p = c.p
     let disc = n.sons[0].sym
     if disc.loc.r == nil: fillObjectFields(c.p.module, typ)
     if disc.loc.t == nil:
-      internalError(n.info, "genTraverseProc()")
+      internalError(c.p.config, n.info, "genTraverseProc()")
     lineF(p, cpsStmts, "switch ($1.$2) {$n", [accessor, disc.loc.r])
     for i in countup(1, sonsLen(n) - 1):
       let branch = n.sons[i]
@@ -51,9 +51,9 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, n: PNode;
     if field.typ.kind == tyVoid: return
     if field.loc.r == nil: fillObjectFields(c.p.module, typ)
     if field.loc.t == nil:
-      internalError(n.info, "genTraverseProc()")
+      internalError(c.p.config, n.info, "genTraverseProc()")
     genTraverseProc(c, "$1.$2" % [accessor, field.loc.r], field.loc.t)
-  else: internalError(n.info, "genTraverseProc()")
+  else: internalError(c.p.config, n.info, "genTraverseProc()")
 
 proc parentObj(accessor: Rope; m: BModule): Rope {.inline.} =
   if not m.compileToCpp:
@@ -72,12 +72,12 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType) =
   of tyArray:
     let arraySize = lengthOrd(typ.sons[0])
     var i: TLoc
-    getTemp(p, getSysType(tyInt), i)
+    getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo(), tyInt), i)
     let oldCode = p.s(cpsStmts)
     linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n",
             i.r, arraySize.rope)
     let oldLen = p.s(cpsStmts).len
-    genTraverseProc(c, rfmt(nil, "$1[$2]", accessor, i.r), typ.sons[1])
+    genTraverseProc(c, ropecg(c.p.module, "$1[$2]", accessor, i.r), typ.sons[1])
     if p.s(cpsStmts).len == oldLen:
       # do not emit dummy long loops for faster debug builds:
       p.s(cpsStmts) = oldCode
@@ -92,12 +92,12 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType) =
   of tyTuple:
     let typ = getUniqueType(typ)
     for i in countup(0, sonsLen(typ) - 1):
-      genTraverseProc(c, rfmt(nil, "$1.Field$2", accessor, i.rope), typ.sons[i])
+      genTraverseProc(c, ropecg(c.p.module, "$1.Field$2", accessor, i.rope), typ.sons[i])
   of tyRef, tyString, tySequence:
     lineCg(p, cpsStmts, c.visitorFrmt, accessor)
   of tyProc:
     if typ.callConv == ccClosure:
-      lineCg(p, cpsStmts, c.visitorFrmt, rfmt(nil, "$1.ClE_0", accessor))
+      lineCg(p, cpsStmts, c.visitorFrmt, ropecg(c.p.module, "$1.ClE_0", accessor))
   else:
     discard
 
@@ -105,7 +105,7 @@ proc genTraverseProcSeq(c: TTraversalClosure, accessor: Rope, typ: PType) =
   var p = c.p
   assert typ.kind == tySequence
   var i: TLoc
-  getTemp(p, getSysType(tyInt), i)
+  getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo(), tyInt), i)
   let oldCode = p.s(cpsStmts)
   lineF(p, cpsStmts, "for ($1 = 0; $1 < $2->$3; $1++) {$n",
       [i.r, accessor, rope(if c.p.module.compileToCpp: "len" else: "Sup.len")])
@@ -157,7 +157,7 @@ proc genTraverseProcForGlobal(m: BModule, s: PSym; info: TLineInfo): Rope =
   var sLoc = s.loc.r
   result = getTempName(m)
 
-  if sfThread in s.flags and emulatedThreadVars():
+  if sfThread in s.flags and emulatedThreadVars(m.config):
     accessThreadLocalVar(p, s)
     sLoc = "NimTV_->" & sLoc
 
diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim
index 56fb379bd..7b44cddad 100644
--- a/compiler/ccgtypes.nim
+++ b/compiler/ccgtypes.nim
@@ -122,7 +122,7 @@ proc getTypeName(m: BModule; typ: PType; sig: SigHash): Rope =
       # check consistency:
       assert($typ.loc.r == $(typ.typeName & $sig))
   result = typ.loc.r
-  if result == nil: internalError("getTypeName: " & $typ.kind)
+  if result == nil: internalError(m.config, "getTypeName: " & $typ.kind)
 
 proc mapSetType(typ: PType): TCTypeKind =
   case int(getSize(typ))
@@ -142,7 +142,7 @@ proc mapType(typ: PType): TCTypeKind =
   of tyOpenArray, tyArray, tyVarargs: result = ctArray
   of tyObject, tyTuple: result = ctStruct
   of tyUserTypeClasses:
-    internalAssert typ.isResolvedUserTypeClass
+    doAssert typ.isResolvedUserTypeClass
     return mapType(typ.lastSon)
   of tyGenericBody, tyGenericInst, tyGenericParam, tyDistinct, tyOrdinal,
      tyTypeDesc, tyAlias, tySink, tyInferred:
@@ -156,7 +156,7 @@ proc mapType(typ: PType): TCTypeKind =
       of 2: result = ctUInt16
       of 4: result = ctInt32
       of 8: result = ctInt64
-      else: internalError("mapType")
+      else: result = ctInt32
   of tyRange: result = mapType(typ.sons[0])
   of tyPtr, tyVar, tyLent, tyRef, tyOptAsRef:
     var base = skipTypes(typ.lastSon, typedescInst)
@@ -183,8 +183,8 @@ proc mapType(typ: PType): TCTypeKind =
     result = TCTypeKind(ord(typ.kind) - ord(tyInt) + ord(ctInt))
   of tyStatic:
     if typ.n != nil: result = mapType(lastSon typ)
-    else: internalError("mapType")
-  else: internalError("mapType")
+    else: doAssert(false, "mapType")
+  else: doAssert(false, "mapType")
 
 proc mapReturnType(typ: PType): TCTypeKind =
   #if skipTypes(typ, typedescInst).kind == tyArray: result = ctPtr
@@ -239,7 +239,7 @@ proc cacheGetType(tab: TypeCache; sig: SigHash): Rope =
   result = tab.getOrDefault(sig)
 
 proc addAbiCheck(m: BModule, t: PType, name: Rope) =
-  if isDefined("checkabi"):
+  if isDefined(m.config, "checkabi"):
     addf(m.s[cfsTypeInfo], "NIM_CHECK_SIZE($1, $2);$n", [name, rope(getSize(t))])
 
 proc ccgIntroducedPtr(s: PSym): bool =
@@ -303,7 +303,7 @@ proc getSimpleTypeDesc(m: BModule, typ: PType): Rope =
   of tyDistinct, tyRange, tyOrdinal: result = getSimpleTypeDesc(m, typ.sons[0])
   of tyStatic:
     if typ.n != nil: result = getSimpleTypeDesc(m, lastSon typ)
-    else: internalError("tyStatic for getSimpleTypeDesc")
+    else: internalError(m.config, "tyStatic for getSimpleTypeDesc")
   of tyGenericInst, tyAlias, tySink:
     result = getSimpleTypeDesc(m, lastSon typ)
   else: result = nil
@@ -347,7 +347,7 @@ proc getTypeForward(m: BModule, typ: PType; sig: SigHash): Rope =
     else:
       pushType(m, concrete)
     doAssert m.forwTypeCache[sig] == result
-  else: internalError("getTypeForward(" & $typ.kind & ')')
+  else: internalError(m.config, "getTypeForward(" & $typ.kind & ')')
 
 proc getTypeDescWeak(m: BModule; t: PType; check: var IntSet): Rope =
   ## like getTypeDescAux but creates only a *weak* dependency. In other words
@@ -389,7 +389,7 @@ proc genProcParams(m: BModule, t: PType, rettype, params: var Rope,
   else:
     rettype = getTypeDescAux(m, t.sons[0], check)
   for i in countup(1, sonsLen(t.n) - 1):
-    if t.n.sons[i].kind != nkSym: internalError(t.n.info, "genProcParams")
+    if t.n.sons[i].kind != nkSym: internalError(m.config, t.n.info, "genProcParams")
     var param = t.n.sons[i].sym
     if isCompileTimeOnly(param.typ): continue
     if params != nil: add(params, ~", ")
@@ -442,7 +442,7 @@ proc mangleRecFieldName(m: BModule; field: PSym, rectype: PType): Rope =
     result = field.loc.r
   else:
     result = rope(mangleField(m, field.name))
-  if result == nil: internalError(field.info, "mangleRecFieldName")
+  if result == nil: internalError(m.config, field.info, "mangleRecFieldName")
 
 proc genRecordFieldsAux(m: BModule, n: PNode,
                         accessExpr: Rope, rectype: PType,
@@ -453,7 +453,7 @@ proc genRecordFieldsAux(m: BModule, n: PNode,
     for i in countup(0, sonsLen(n) - 1):
       add(result, genRecordFieldsAux(m, n.sons[i], accessExpr, rectype, check))
   of nkRecCase:
-    if n.sons[0].kind != nkSym: internalError(n.info, "genRecordFieldsAux")
+    if n.sons[0].kind != nkSym: internalError(m.config, n.info, "genRecordFieldsAux")
     add(result, genRecordFieldsAux(m, n.sons[0], accessExpr, rectype, check))
     let uname = rope(mangle(n.sons[0].sym.name.s) & 'U')
     let ae = if accessExpr != nil: "$1.$2" % [accessExpr, uname]
@@ -481,7 +481,7 @@ proc genRecordFieldsAux(m: BModule, n: PNode,
               addf(unionBody, "#pragma pack(pop)$n", [])
         else:
           add(unionBody, genRecordFieldsAux(m, k, ae, rectype, check))
-      else: internalError("genRecordFieldsAux(record case branch)")
+      else: internalError(m.config, "genRecordFieldsAux(record case branch)")
     if unionBody != nil:
       addf(result, "union{$n$1} $2;$n", [unionBody, uname])
   of nkSym:
@@ -509,7 +509,7 @@ proc genRecordFieldsAux(m: BModule, n: PNode,
         # don't use fieldType here because we need the
         # tyGenericInst for C++ template support
         addf(result, "$1 $2;$n", [getTypeDescAux(m, field.loc.t, check), sname])
-  else: internalError(n.info, "genRecordFieldsAux()")
+  else: internalError(m.config, n.info, "genRecordFieldsAux()")
 
 proc getRecordFields(m: BModule, typ: PType, check: var IntSet): Rope =
   result = genRecordFieldsAux(m, typ.n, nil, typ, check)
@@ -556,7 +556,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("popCurrentExceptionEx")) & ";" & rnl & result
+          result = genProcHeader(m, magicsys.getCompilerProc(m.g.graph, "popCurrentExceptionEx")) & ";" & rnl & result
       hasField = true
     else:
       appcg(m, result, " {$n  $1 Sup;$n",
@@ -606,7 +606,7 @@ proc resolveStarsInCppType(typ: PType, idx, stars: int): PType =
   # Make sure the index refers to one of the generic params of the type.
   # XXX: we should catch this earlier and report it as a semantic error.
   if idx >= typ.len:
-    internalError "invalid apostrophe type parameter index"
+    doAssert false, "invalid apostrophe type parameter index"
 
   result = typ.sons[idx]
   for i in 1..stars:
@@ -619,7 +619,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
   var t = origTyp.skipTypes(irrelevantForBackend)
   if containsOrIncl(check, t.id):
     if not (isImportedCppType(origTyp) or isImportedCppType(t)):
-      internalError("cannot generate C type for: " & typeToString(origTyp))
+      internalError(m.config, "cannot generate C type for: " & typeToString(origTyp))
     # XXX: this BUG is hard to fix -> we need to introduce helper structs,
     # but determining when this needs to be done is hard. We should split
     # C type generation into an analysis and a code generation phase somehow.
@@ -697,7 +697,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
           of 2: addf(m.s[cfsTypes], "typedef NU16 $1;$n", [result])
           of 4: addf(m.s[cfsTypes], "typedef NI32 $1;$n", [result])
           of 8: addf(m.s[cfsTypes], "typedef NI64 $1;$n", [result])
-          else: internalError(t.sym.info, "getTypeDescAux: enum")
+          else: internalError(m.config, t.sym.info, "getTypeDescAux: enum")
         when false:
           let owner = hashOwner(t.sym)
           if not gDebugInfo.hasEnum(t.sym.name.s, t.sym.info.line, owner):
@@ -808,7 +808,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
             if typeInSlot == nil or typeInSlot.kind == tyVoid:
               result.add(~"void")
             elif typeInSlot.kind == tyStatic:
-              internalAssert typeInSlot.n != nil
+              internalAssert m.config, typeInSlot.n != nil
               result.add typeInSlot.n.renderTree
             else:
               result.add getTypeDescAux(m, typeInSlot, check)
@@ -875,7 +875,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
      tyUserTypeClass, tyUserTypeClassInst, tyInferred:
     result = getTypeDescAux(m, lastSon(t), check)
   else:
-    internalError("getTypeDescAux(" & $t.kind & ')')
+    internalError(m.config, "getTypeDescAux(" & $t.kind & ')')
     result = nil
   # fixes bug #145:
   excl(check, t.id)
@@ -915,7 +915,7 @@ template cgDeclFrmt*(s: PSym): string = s.constraint.strVal
 proc genProcHeader(m: BModule, prc: PSym): Rope =
   var
     rettype, params: Rope
-  genCLineDir(result, prc.info)
+  genCLineDir(result, prc.info, m.config)
   # using static is needed for inline procs
   if lfExportLib in prc.loc.flags:
     if isHeaderFile in m.flags:
@@ -968,7 +968,7 @@ proc genTypeInfoAuxBase(m: BModule; typ, origType: PType;
   if flags != 0:
     addf(m.s[cfsTypeInit3], "$1.flags = $2;$n", [name, rope(flags)])
   discard cgsym(m, "TNimType")
-  if isDefined("nimTypeNames"):
+  if isDefined(m.config, "nimTypeNames"):
     var typename = typeToString(if origType.typeInst != nil: origType.typeInst
                                 else: origType, preferName)
     if typename == "ref object" and origType.skipTypes(skipPtrs).sym != nil:
@@ -1000,7 +1000,7 @@ proc discriminatorTableName(m: BModule, objtype: PType, d: PSym): Rope =
   while lookupInRecord(objtype.n, d.name) == nil:
     objtype = objtype.sons[0]
   if objtype.sym == nil:
-    internalError(d.info, "anonymous obj with discriminator")
+    internalError(m.config, d.info, "anonymous obj with discriminator")
   result = "NimDT_$1_$2" % [rope($hashType(objtype)), rope(d.name.s.mangle)]
 
 proc discriminatorTableDecl(m: BModule, objtype: PType, d: PSym): Rope =
@@ -1034,7 +1034,7 @@ proc genObjectFields(m: BModule, typ, origType: PType, n: PNode, expr: Rope;
     assert L > 0
     if field.loc.r == nil: fillObjectFields(m, typ)
     if field.loc.t == nil:
-      internalError(n.info, "genObjectFields")
+      internalError(m.config, n.info, "genObjectFields")
     addf(m.s[cfsTypeInit3], "$1.kind = 3;$n" &
         "$1.offset = offsetof($2, $3);$n" & "$1.typ = $4;$n" &
         "$1.name = $5;$n" & "$1.sons = &$6[0];$n" &
@@ -1050,7 +1050,7 @@ proc genObjectFields(m: BModule, typ, origType: PType, n: PNode, expr: Rope;
       case b.kind
       of nkOfBranch:
         if sonsLen(b) < 2:
-          internalError(b.info, "genObjectFields; nkOfBranch broken")
+          internalError(m.config, b.info, "genObjectFields; nkOfBranch broken")
         for j in countup(0, sonsLen(b) - 2):
           if b.sons[j].kind == nkRange:
             var x = int(getOrdValue(b.sons[j].sons[0]))
@@ -1064,23 +1064,23 @@ proc genObjectFields(m: BModule, typ, origType: PType, n: PNode, expr: Rope;
       of nkElse:
         addf(m.s[cfsTypeInit3], "$1[$2] = &$3;$n",
              [tmp, rope(L), tmp2])
-      else: internalError(n.info, "genObjectFields(nkRecCase)")
+      else: internalError(m.config, n.info, "genObjectFields(nkRecCase)")
   of nkSym:
     var field = n.sym
     if field.bitsize == 0:
       if field.loc.r == nil: fillObjectFields(m, typ)
       if field.loc.t == nil:
-        internalError(n.info, "genObjectFields")
+        internalError(m.config, n.info, "genObjectFields")
       addf(m.s[cfsTypeInit3], "$1.kind = 1;$n" &
           "$1.offset = offsetof($2, $3);$n" & "$1.typ = $4;$n" &
           "$1.name = $5;$n", [expr, getTypeDesc(m, origType),
           field.loc.r, genTypeInfo(m, field.typ, info), makeCString(field.name.s)])
-  else: internalError(n.info, "genObjectFields")
+  else: internalError(m.config, n.info, "genObjectFields")
 
 proc genObjectInfo(m: BModule, typ, origType: PType, name: Rope; info: TLineInfo) =
   if typ.kind == tyObject:
     if incompleteType(typ):
-      localError(info, "request for RTTI generation for incomplete object: " &
+      localError(m.config, info, "request for RTTI generation for incomplete object: " &
                         typeToString(typ))
     genTypeInfoAux(m, typ, origType, name, info)
   else:
@@ -1171,12 +1171,12 @@ proc genSetInfo(m: BModule, typ: PType, name: Rope; info: TLineInfo) =
 proc genArrayInfo(m: BModule, typ: PType, name: Rope; info: TLineInfo) =
   genTypeInfoAuxBase(m, typ, typ, name, genTypeInfo(m, typ.sons[1], info), info)
 
-proc fakeClosureType(owner: PSym): PType =
+proc fakeClosureType(m: BModule; owner: PSym): PType =
   # we generate the same RTTI as for a tuple[pointer, ref tuple[]]
   result = newType(tyTuple, owner)
   result.rawAddSon(newType(tyPointer, owner))
   var r = newType(tyRef, owner)
-  let obj = createObj(owner, owner.info, final=false)
+  let obj = createObj(m.g.graph, owner, owner.info, final=false)
   r.rawAddSon(obj)
   result.rawAddSon(r)
 
@@ -1227,19 +1227,19 @@ proc genTypeInfo(m: BModule, t: PType; info: TLineInfo): Rope =
     genTypeInfoAuxBase(m, t, t, result, rope"0", info)
   of tyStatic:
     if t.n != nil: result = genTypeInfo(m, lastSon t, info)
-    else: internalError("genTypeInfo(" & $t.kind & ')')
+    else: internalError(m.config, "genTypeInfo(" & $t.kind & ')')
   of tyUserTypeClasses:
-    internalAssert t.isResolvedUserTypeClass
+    internalAssert m.config, t.isResolvedUserTypeClass
     return genTypeInfo(m, t.lastSon, info)
   of tyProc:
     if t.callConv != ccClosure:
       genTypeInfoAuxBase(m, t, t, result, rope"0", info)
     else:
-      let x = fakeClosureType(t.owner)
+      let x = fakeClosureType(m, t.owner)
       genTupleInfo(m, x, x, result, info)
   of tySequence, tyRef, tyOptAsRef:
     genTypeInfoAux(m, t, t, result, info)
-    if gSelectedGC >= gcMarkAndSweep:
+    if m.config.selectedGC >= gcMarkAndSweep:
       let markerProc = genTraverseProc(m, origType, sig)
       addf(m.s[cfsTypeInit3], "$1.marker = $2;$n", [result, markerProc])
   of tyPtr, tyRange: genTypeInfoAux(m, t, t, result, info)
@@ -1253,7 +1253,7 @@ proc genTypeInfo(m: BModule, t: PType; info: TLineInfo): Rope =
     # BUGFIX: use consistently RTTI without proper field names; otherwise
     # results are not deterministic!
     genTupleInfo(m, t, origType, result, info)
-  else: internalError("genTypeInfo(" & $t.kind & ')')
+  else: internalError(m.config, "genTypeInfo(" & $t.kind & ')')
   if t.deepCopy != nil:
     genDeepCopyProc(m, t.deepCopy, result)
   elif origType.deepCopy != nil:
diff --git a/compiler/ccgutils.nim b/compiler/ccgutils.nim
index fe28d2209..a6080a808 100644
--- a/compiler/ccgutils.nim
+++ b/compiler/ccgutils.nim
@@ -53,7 +53,7 @@ proc hashString*(s: string): BiggestInt =
     result = a
 
 var
-  gTypeTable: array[TTypeKind, TIdTable]
+  gTypeTable: array[TTypeKind, TIdTable]  # XXX globals here
   gCanonicalTypes: array[TTypeKind, PType]
 
 proc initTypeTables() =
@@ -211,8 +211,4 @@ proc mangle*(name: string): string =
   if requiresUnderscore:
     result.add "_"
 
-proc emitLazily*(s: PSym): bool {.inline.} =
-  result = optDeadCodeElim in gGlobalOptions or
-           sfDeadCodeElim in getModule(s).flags
-
 initTypeTables()
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index 4d3cabd3a..79bcf0491 100644
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -19,6 +19,7 @@ import
 import strutils except `%` # collides with ropes.`%`
 
 from modulegraphs import ModuleGraph
+from configuration import warnGcMem, errXMustBeCompileTime, hintDependency, errGenerated
 import dynlib
 
 when not declared(dynlib.libCandidates):
@@ -92,6 +93,7 @@ proc useHeader(m: BModule, sym: PSym) =
 proc cgsym(m: BModule, name: string): Rope
 
 proc ropecg(m: BModule, frmt: FormatStr, args: varargs[Rope]): Rope =
+  assert m != nil
   var i = 0
   var length = len(frmt)
   result = nil
@@ -115,15 +117,15 @@ proc ropecg(m: BModule, frmt: FormatStr, args: varargs[Rope]): Rope =
           if i >= length or not (frmt[i] in {'0'..'9'}): break
         num = j
         if j > high(args) + 1:
-          internalError("ropes: invalid format string $" & $j)
+          internalError(m.config, "ropes: invalid format string $" & $j)
         add(result, args[j-1])
       of 'n':
-        if optLineDir notin gOptions: add(result, rnl)
+        if optLineDir notin m.config.options: add(result, rnl)
         inc(i)
       of 'N':
         add(result, rnl)
         inc(i)
-      else: internalError("ropes: invalid format string $" & frmt[i])
+      else: internalError(m.config, "ropes: invalid format string $" & frmt[i])
     elif frmt[i] == '#' and frmt[i+1] in IdentStartChars:
       inc(i)
       var j = i
@@ -145,15 +147,10 @@ proc ropecg(m: BModule, frmt: FormatStr, args: varargs[Rope]): Rope =
     if i - 1 >= start:
       add(result, substr(frmt, start, i - 1))
 
-template rfmt(m: BModule, fmt: string, args: varargs[Rope]): untyped =
-  ropecg(m, fmt, args)
-
-var indent = "\t".rope
-
 proc indentLine(p: BProc, r: Rope): Rope =
   result = r
   for i in countup(0, p.blocks.len-1):
-    prepend(result, indent)
+    prepend(result, "\t".rope)
 
 proc appcg(m: BModule, c: var Rope, frmt: FormatStr,
            args: varargs[Rope]) =
@@ -189,14 +186,14 @@ proc safeLineNm(info: TLineInfo): int =
   result = toLinenumber(info)
   if result < 0: result = 0 # negative numbers are not allowed in #line
 
-proc genCLineDir(r: var Rope, filename: string, line: int) =
+proc genCLineDir(r: var Rope, filename: string, line: int; conf: ConfigRef) =
   assert line >= 0
-  if optLineDir in gOptions:
+  if optLineDir in conf.options:
     addf(r, "$N#line $2 $1$N",
         [rope(makeSingleLineCString(filename)), rope(line)])
 
-proc genCLineDir(r: var Rope, info: TLineInfo) =
-  genCLineDir(r, info.toFullPath, info.safeLineNm)
+proc genCLineDir(r: var Rope, info: TLineInfo; conf: ConfigRef) =
+  genCLineDir(r, info.toFullPath, info.safeLineNm, conf)
 
 proc freshLineInfo(p: BProc; info: TLineInfo): bool =
   if p.lastLineInfo.line != info.line or
@@ -213,9 +210,9 @@ proc genLineDir(p: BProc, t: PNode) =
     tt = tt.sons[1]
   let line = tt.info.safeLineNm
 
-  if optEmbedOrigSrc in gGlobalOptions:
-    add(p.s(cpsStmts), ~"//" & tt.info.sourceLine & rnl)
-  genCLineDir(p.s(cpsStmts), tt.info.toFullPath, line)
+  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)
   if ({optStackTrace, optEndb} * p.options == {optStackTrace, optEndb}) and
       (p.prc == nil or sfPure notin p.prc.flags):
     if freshLineInfo(p, tt.info):
@@ -223,20 +220,20 @@ proc genLineDir(p: BProc, t: PNode) =
               line.rope, makeCString(toFilename(tt.info)))
   elif ({optLineTrace, optStackTrace} * p.options ==
       {optLineTrace, optStackTrace}) and
-      (p.prc == nil or sfPure notin p.prc.flags) and tt.info.fileIndex >= 0:
+      (p.prc == nil or sfPure notin p.prc.flags) and tt.info.fileIndex != InvalidFileIDX:
     if freshLineInfo(p, tt.info):
       linefmt(p, cpsStmts, "nimln_($1, $2);$n",
-              line.rope, tt.info.quotedFilename)
+              line.rope, quotedFilename(p.config, tt.info))
 
 proc postStmtActions(p: BProc) {.inline.} =
   add(p.s(cpsStmts), p.module.injectStmt)
 
 proc accessThreadLocalVar(p: BProc, s: PSym)
-proc emulatedThreadVars(): bool {.inline.}
+proc emulatedThreadVars(conf: ConfigRef): bool {.inline.}
 proc genProc(m: BModule, prc: PSym)
 
 template compileToCpp(m: BModule): untyped =
-  gCmd == cmdCompileToCpp or sfCompileToCpp in m.module.flags
+  m.config.cmd == cmdCompileToCpp or sfCompileToCpp in m.module.flags
 
 proc getTempName(m: BModule): Rope =
   result = m.tmpBase & rope(m.labels)
@@ -265,7 +262,7 @@ proc rdCharLoc(a: TLoc): Rope =
 
 proc genObjectInit(p: BProc, section: TCProcSection, t: PType, a: TLoc,
                    takeAddr: bool) =
-  if p.module.compileToCpp and t.isException:
+  if p.module.compileToCpp and t.isException and not isDefined(p.config, "noCppExceptions"):
     # init vtable in Exception object for polymorphic exceptions
     includeHeader(p.module, "<new>")
     linefmt(p, section, "new ($1) $2;$n", rdLoc(a), getTypeDesc(p.module, t))
@@ -373,7 +370,7 @@ proc getIntTemp(p: BProc, result: var TLoc) =
   linefmt(p, cpsLocals, "NI $1;$n", result.r)
   result.k = locTemp
   result.storage = OnStack
-  result.lode = lodeTyp getSysType(tyInt)
+  result.lode = lodeTyp getSysType(p.module.g.graph, unknownLineInfo(), tyInt)
   result.flags = {}
 
 proc initGCFrame(p: BProc): Rope =
@@ -417,7 +414,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 gOptions: "" else: tnl
+  let nl = if optLineDir in p.config.options: "" else: tnl
   let decl = localVarDecl(p, n) & ";" & nl
   line(p, cpsLocals, decl)
   localDebugInfo(p, n.sym)
@@ -511,24 +508,24 @@ proc initFrame(p: BProc, procname, filename: Rope): Rope =
   discard cgsym(p.module, "nimFrame")
   if p.maxFrameLen > 0:
     discard cgsym(p.module, "VarSlot")
-    result = rfmt(nil, "\tnimfrs_($1, $2, $3, $4);$n",
+    result = ropecg(p.module, "\tnimfrs_($1, $2, $3, $4);$n",
                   procname, filename, p.maxFrameLen.rope,
                   p.blocks[0].frameLen.rope)
   else:
-    result = rfmt(nil, "\tnimfr_($1, $2);$n", procname, filename)
+    result = ropecg(p.module, "\tnimfr_($1, $2);$n", procname, filename)
 
 proc initFrameNoDebug(p: BProc; frame, procname, filename: Rope; line: int): Rope =
   discard cgsym(p.module, "nimFrame")
   addf(p.blocks[0].sections[cpsLocals], "TFrame $1;$n", [frame])
-  result = rfmt(nil, "\t$1.procname = $2; $1.filename = $3; " &
+  result = ropecg(p.module, "\t$1.procname = $2; $1.filename = $3; " &
                       " $1.line = $4; $1.len = -1; nimFrame(&$1);$n",
                       frame, procname, filename, rope(line))
 
 proc deinitFrameNoDebug(p: BProc; frame: Rope): Rope =
-  result = rfmt(p.module, "\t#popFrameOfAddr(&$1);$n", frame)
+  result = ropecg(p.module, "\t#popFrameOfAddr(&$1);$n", frame)
 
 proc deinitFrame(p: BProc): Rope =
-  result = rfmt(p.module, "\t#popFrame();$n")
+  result = ropecg(p.module, "\t#popFrame();$n")
 
 include ccgexprs
 
@@ -551,7 +548,7 @@ proc loadDynamicLib(m: BModule, lib: PLib) =
     if lib.path.kind in {nkStrLit..nkTripleStrLit}:
       var s: TStringSeq = @[]
       libCandidates(lib.path.strVal, s)
-      rawMessage(hintDependency, lib.path.strVal)
+      rawMessage(m.config, hintDependency, lib.path.strVal)
       var loadlib: Rope = nil
       for i in countup(0, high(s)):
         inc(m.labels)
@@ -575,7 +572,7 @@ proc loadDynamicLib(m: BModule, lib: PLib) =
            "if (!($1 = #nimLoadLibrary($2))) #nimLoadLibraryError($2);$n",
            [tmp, rdLoc(dest)])
 
-  if lib.name == nil: internalError("loadDynamicLib")
+  if lib.name == nil: internalError(m.config, "loadDynamicLib")
 
 proc mangleDynLibProc(sym: PSym): Rope =
   if sfCompilerProc in sym.flags:
@@ -606,14 +603,14 @@ proc symInDynamicLib(m: BModule, sym: PSym) =
         [tmp, getTypeDesc(m, sym.typ), params, makeCString($extname)]
     var last = lastSon(n)
     if last.kind == nkHiddenStdConv: last = last.sons[1]
-    internalAssert(last.kind == nkStrLit)
+    internalAssert(m.config, last.kind == nkStrLit)
     let idx = last.strVal
     if idx.len == 0:
       add(m.initProc.s(cpsStmts), load)
     elif idx.len == 1 and idx[0] in {'0'..'9'}:
       add(m.extensionLoaders[idx[0]], load)
     else:
-      internalError(sym.info, "wrong index: " & idx)
+      internalError(m.config, sym.info, "wrong index: " & idx)
   else:
     appcg(m, m.s[cfsDynLibInit],
         "\t$1 = ($2) #nimGetProcAddr($3, $4);$n",
@@ -639,18 +636,18 @@ proc symInDynamicLibPartial(m: BModule, sym: PSym) =
   sym.typ.sym = nil           # generate a new name
 
 proc cgsym(m: BModule, name: string): Rope =
-  let sym = magicsys.getCompilerProc(name)
+  let sym = magicsys.getCompilerProc(m.g.graph, name)
   if sym != nil:
     case sym.kind
     of skProc, skFunc, skMethod, skConverter, skIterator: genProc(m, sym)
     of skVar, skResult, skLet: genVarPrototype(m, newSymNode sym)
     of skType: discard getTypeDesc(m, sym.typ)
-    else: internalError("cgsym: " & name & ": " & $sym.kind)
+    else: internalError(m.config, "cgsym: " & name & ": " & $sym.kind)
   else:
     # we used to exclude the system module from this check, but for DLL
     # generation support this sloppyness leads to hard to detect bugs, so
     # we're picky here for the system module too:
-    rawMessage(errSystemNeeds, name)
+    rawMessage(m.config, errGenerated, "system module needs: " & name)
   result = sym.loc.r
 
 proc generateHeaders(m: BModule) =
@@ -678,7 +675,7 @@ proc generateHeaders(m: BModule) =
 
 proc openNamespaceNim(): Rope =
   result.add("namespace Nim {" & tnl)
-  
+
 proc closeNamespaceNim(): Rope =
   result.add("}" & tnl)
 
@@ -687,7 +684,7 @@ proc closureSetup(p: BProc, prc: PSym) =
   # prc.ast[paramsPos].last contains the type we're after:
   var ls = lastSon(prc.ast[paramsPos])
   if ls.kind != nkSym:
-    internalError(prc.info, "closure generation failed")
+    internalError(p.config, prc.info, "closure generation failed")
   var env = ls.sym
   #echo "created environment: ", env.id, " for ", prc.name.s
   assignLocalVar(p, ls)
@@ -727,7 +724,7 @@ proc genProcAux(m: BModule, prc: PSym) =
   assert(prc.ast != nil)
   if sfPure notin prc.flags and prc.typ.sons[0] != nil:
     if resultPos >= prc.ast.len:
-      internalError(prc.info, "proc has no result symbol")
+      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]):
@@ -742,11 +739,11 @@ proc genProcAux(m: BModule, prc: PSym) =
         assignLocalVar(p, resNode)
         assert(res.loc.r != nil)
         initLocalVar(p, res, immediateAsgn=false)
-      returnStmt = rfmt(nil, "\treturn $1;$n", rdLoc(res.loc))
+      returnStmt = ropecg(p.module, "\treturn $1;$n", rdLoc(res.loc))
     else:
       fillResult(resNode)
       assignParam(p, res)
-      resetLoc(p, res.loc)
+      if sfNoInit notin prc.flags: resetLoc(p, res.loc)
       if skipTypes(res.typ, abstractInst).kind == tyArray:
         #incl(res.loc.flags, lfIndirect)
         res.loc.storage = OnUnknown
@@ -764,15 +761,15 @@ proc genProcAux(m: BModule, prc: PSym) =
   if sfPure in prc.flags:
     if hasDeclspec in extccomp.CC[extccomp.cCompiler].props:
       header = "__declspec(naked) " & header
-    generatedProc = rfmt(nil, "$N$1 {$n$2$3$4}$N$N",
+    generatedProc = ropecg(p.module, "$N$1 {$n$2$3$4}$N$N",
                          header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts))
   else:
-    generatedProc = rfmt(nil, "$N$1 {$N", header)
+    generatedProc = ropecg(p.module, "$N$1 {$N", header)
     add(generatedProc, initGCFrame(p))
     if optStackTrace in prc.options:
       add(generatedProc, p.s(cpsLocals))
       var procname = makeCString(prc.name.s)
-      add(generatedProc, initFrame(p, procname, prc.info.quotedFilename))
+      add(generatedProc, initFrame(p, procname, quotedFilename(p.config, prc.info)))
     else:
       add(generatedProc, p.s(cpsLocals))
     if optProfiler in prc.options:
@@ -791,10 +788,10 @@ proc genProcAux(m: BModule, prc: PSym) =
 proc requiresExternC(m: BModule; sym: PSym): bool {.inline.} =
   result = (sfCompileToCpp in m.module.flags and
            sfCompileToCpp notin sym.getModule().flags and
-           gCmd != cmdCompileToCpp) or (
+           m.config.cmd != cmdCompileToCpp) or (
            sym.flags * {sfImportc, sfInfixCall, sfCompilerProc} == {sfImportc} and
            sym.magic == mNone and
-           gCmd == cmdCompileToCpp)
+           m.config.cmd == cmdCompileToCpp)
 
 proc genProcPrototype(m: BModule, sym: PSym) =
   useHeader(m, sym)
@@ -802,7 +799,7 @@ proc genProcPrototype(m: BModule, sym: PSym) =
   if lfDynamicLib in sym.loc.flags:
     if getModule(sym).id != m.module.id and
         not containsOrIncl(m.declaredThings, sym.id):
-      add(m.s[cfsVars], rfmt(nil, "extern $1 $2;$n",
+      add(m.s[cfsVars], ropecg(m, "extern $1 $2;$n",
                         getTypeDesc(m, sym.loc.t), mangleDynLibProc(sym)))
   elif not containsOrIncl(m.declaredProtos, sym.id):
     var header = genProcHeader(m, sym)
@@ -814,7 +811,7 @@ proc genProcPrototype(m: BModule, sym: PSym) =
       header.add(" __attribute__((naked))")
     if sfNoReturn in sym.flags and hasAttribute in CC[cCompiler].props:
       header.add(" __attribute__((noreturn))")
-    add(m.s[cfsProcHeaders], rfmt(nil, "$1;$n", header))
+    add(m.s[cfsProcHeaders], ropecg(m, "$1;$n", header))
 
 proc genProcNoForward(m: BModule, prc: PSym) =
   if lfImportCompilerProc in prc.loc.flags:
@@ -920,14 +917,14 @@ proc genVarPrototype(m: BModule, n: PNode) =
       if sfVolatile in sym.flags: add(m.s[cfsVars], " volatile")
       addf(m.s[cfsVars], " $1;$n", [sym.loc.r])
 
-proc addIntTypes(result: var Rope) {.inline.} =
+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 useNimNamespace : result.add("#define USE_NIM_NAMESPACE" & tnl)
+  if optUseNimNamespace in conf.globalOptions: result.add("#define USE_NIM_NAMESPACE" & tnl)
 
-proc getCopyright(cfile: Cfile): Rope =
-  if optCompileOnly in gGlobalOptions:
+proc getCopyright(conf: ConfigRef; cfile: Cfile): Rope =
+  if optCompileOnly in conf.globalOptions:
     result = ("/* Generated by Nim Compiler v$1 */$N" &
         "/*   (c) " & copyrightYear & " Andreas Rumpf */$N" &
         "/* The generated code is subject to the original license. */$N") %
@@ -942,11 +939,11 @@ proc getCopyright(cfile: Cfile): Rope =
         rope(platform.OS[targetOS].name),
         rope(platform.CPU[targetCPU].name),
         rope(extccomp.CC[extccomp.cCompiler].name),
-        rope(getCompileCFileCmd(cfile))]
+        rope(getCompileCFileCmd(conf, cfile))]
 
-proc getFileHeader(cfile: Cfile): Rope =
-  result = getCopyright(cfile)
-  addIntTypes(result)
+proc getFileHeader(conf: ConfigRef; cfile: Cfile): Rope =
+  result = getCopyright(conf, cfile)
+  addIntTypes(result, conf)
 
 proc genFilenames(m: BModule): Rope =
   discard cgsym(m, "dbgRegisterFilename")
@@ -1051,8 +1048,8 @@ proc genMainProc(m: BModule) =
 
   var nimMain, otherMain: FormatStr
   if platform.targetOS == osWindows and
-      gGlobalOptions * {optGenGuiApp, optGenDynLib} != {}:
-    if optGenGuiApp in gGlobalOptions:
+      m.config.globalOptions * {optGenGuiApp, optGenDynLib} != {}:
+    if optGenGuiApp in m.config.globalOptions:
       nimMain = WinNimMain
       otherMain = WinCMain
     else:
@@ -1062,7 +1059,7 @@ proc genMainProc(m: BModule) =
   elif platform.targetOS == osGenode:
     nimMain = GenodeNimMain
     otherMain = ComponentConstruct
-  elif optGenDynLib in gGlobalOptions:
+  elif optGenDynLib in m.config.globalOptions:
     nimMain = PosixNimDllMain
     otherMain = PosixCDllMain
   elif platform.targetOS == osStandalone:
@@ -1072,16 +1069,16 @@ proc genMainProc(m: BModule) =
     nimMain = PosixNimMain
     otherMain = PosixCMain
   if m.g.breakpoints != nil: discard cgsym(m, "dbgRegisterBreakpoint")
-  if optEndb in gOptions:
+  if optEndb in m.config.options:
     m.g.breakpoints.add(m.genFilenames)
 
   let initStackBottomCall =
-    if platform.targetOS == osStandalone or gSelectedGC == gcNone: "".rope
+    if platform.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() and platform.targetOS != osStandalone:
+     if emulatedThreadVars(m.config) and platform.targetOS != osStandalone:
        ropecg(m, "\t#initThreadVarsEmulation();$N")
      else:
        "".rope,
@@ -1089,12 +1086,12 @@ proc genMainProc(m: BModule) =
 
   appcg(m, m.s[cfsProcs], nimMain,
         [m.g.mainModInit, initStackBottomCall, rope(m.labels)])
-  if optNoMain notin gGlobalOptions:
-    if useNimNamespace: 
+  if optNoMain notin m.config.globalOptions:
+    if optUseNimNamespace in m.config.globalOptions:
       m.s[cfsProcs].add closeNamespaceNim() & "using namespace Nim;" & tnl
 
     appcg(m, m.s[cfsProcs], otherMain, [])
-    if useNimNamespace: m.s[cfsProcs].add openNamespaceNim()
+    if optUseNimNamespace in m.config.globalOptions: m.s[cfsProcs].add openNamespaceNim()
 
 proc getSomeInitName(m: PSym, suffix: string): Rope =
   assert m.kind == skModule
@@ -1118,8 +1115,8 @@ proc registerModuleToMain(g: BModuleList; m: PSym) =
   var
     init = m.getInitName
     datInit = m.getDatInitName
-  addf(g.mainModProcs, "NIM_EXTERNC N_NOINLINE(void, $1)(void);$N", [init])
-  addf(g.mainModProcs, "NIM_EXTERNC N_NOINLINE(void, $1)(void);$N", [datInit])
+  addf(g.mainModProcs, "N_LIB_PRIVATE N_NIMCALL(void, $1)(void);$N", [init])
+  addf(g.mainModProcs, "N_LIB_PRIVATE N_NIMCALL(void, $1)(void);$N", [datInit])
   if sfSystemModule notin m.flags:
     addf(g.mainDatInit, "\t$1();$N", [datInit])
     let initCall = "\t$1();$N" % [init]
@@ -1130,7 +1127,7 @@ proc registerModuleToMain(g: BModuleList; m: PSym) =
 
 proc genInitCode(m: BModule) =
   var initname = getInitName(m.module)
-  var prc = "NIM_EXTERNC N_NOINLINE(void, $1)(void) {$N" % [initname]
+  var prc = "N_LIB_PRIVATE N_NIMCALL(void, $1)(void) {$N" % [initname]
   if m.typeNodes > 0:
     appcg(m, m.s[cfsTypeInit1], "static #TNimNode $1[$2];$n",
           [m.typeNodesName, rope(m.typeNodes)])
@@ -1140,11 +1137,11 @@ proc genInitCode(m: BModule) =
 
   add(prc, initGCFrame(m.initProc))
 
-  add(prc, genSectionStart(cpsLocals))
+  add(prc, genSectionStart(cpsLocals, m.config))
   add(prc, m.preInitProc.s(cpsLocals))
   add(prc, m.initProc.s(cpsLocals))
   add(prc, m.postInitProc.s(cpsLocals))
-  add(prc, genSectionEnd(cpsLocals))
+  add(prc, genSectionEnd(cpsLocals, m.config))
 
   if optStackTrace in m.initProc.options and frameDeclared notin m.flags:
     # BUT: the generated init code might depend on a current frame, so
@@ -1152,33 +1149,33 @@ proc genInitCode(m: BModule) =
     incl m.flags, frameDeclared
     if preventStackTrace notin m.flags:
       var procname = makeCString(m.module.name.s)
-      add(prc, initFrame(m.initProc, procname, m.module.info.quotedFilename))
+      add(prc, initFrame(m.initProc, procname, quotedFilename(m.config, m.module.info)))
     else:
       add(prc, ~"\tTFrame FR_; FR_.len = 0;$N")
 
-  add(prc, genSectionStart(cpsInit))
+  add(prc, genSectionStart(cpsInit, m.config))
   add(prc, m.preInitProc.s(cpsInit))
   add(prc, m.initProc.s(cpsInit))
   add(prc, m.postInitProc.s(cpsInit))
-  add(prc, genSectionEnd(cpsInit))
+  add(prc, genSectionEnd(cpsInit, m.config))
 
-  add(prc, genSectionStart(cpsStmts))
+  add(prc, genSectionStart(cpsStmts, m.config))
   add(prc, m.preInitProc.s(cpsStmts))
   add(prc, m.initProc.s(cpsStmts))
   add(prc, m.postInitProc.s(cpsStmts))
-  add(prc, genSectionEnd(cpsStmts))
+  add(prc, genSectionEnd(cpsStmts, m.config))
   if optStackTrace in m.initProc.options and preventStackTrace notin m.flags:
     add(prc, deinitFrame(m.initProc))
   add(prc, deinitGCFrame(m.initProc))
   addf(prc, "}$N$N", [])
 
-  prc.addf("NIM_EXTERNC N_NOINLINE(void, $1)(void) {$N",
+  prc.addf("N_LIB_PRIVATE N_NIMCALL(void, $1)(void) {$N",
            [getDatInitName(m.module)])
 
   for i in cfsTypeInit1..cfsDynLibInit:
-    add(prc, genSectionStart(i))
+    add(prc, genSectionStart(i, m.config))
     add(prc, m.s[i])
-    add(prc, genSectionEnd(i))
+    add(prc, genSectionEnd(i, m.config))
 
   addf(prc, "}$N$N", [])
   # we cannot simply add the init proc to ``m.s[cfsProcs]`` anymore because
@@ -1193,18 +1190,19 @@ proc genInitCode(m: BModule) =
       add(m.s[cfsInitProc], ex)
 
 proc genModule(m: BModule, cfile: Cfile): Rope =
-  result = getFileHeader(cfile)
+  result = getFileHeader(m.config, cfile)
   result.add(genMergeInfo(m))
 
   generateThreadLocalStorage(m)
   generateHeaders(m)
   for i in countup(cfsHeaders, cfsProcs):
-    add(result, genSectionStart(i))
+    add(result, genSectionStart(i, m.config))
     add(result, m.s[i])
-    add(result, genSectionEnd(i))
-    if useNimNamespace and i == cfsHeaders: result.add openNamespaceNim()    
+    add(result, genSectionEnd(i, m.config))
+    if optUseNimNamespace in m.config.globalOptions and i == cfsHeaders:
+      result.add openNamespaceNim()
   add(result, m.s[cfsInitProc])
-  if useNimNamespace: result.add closeNamespaceNim()
+  if optUseNimNamespace in m.config.globalOptions: result.add closeNamespaceNim()
 
 proc newPreInitProc(m: BModule): BProc =
   result = newProc(nil, m)
@@ -1217,10 +1215,12 @@ proc newPostInitProc(m: BModule): BProc =
   result.labels = 200_000
 
 proc initProcOptions(m: BModule): TOptions =
-  if sfSystemModule in m.module.flags: gOptions-{optStackTrace} else: gOptions
+  let opts = m.config.options
+  if sfSystemModule in m.module.flags: opts-{optStackTrace} else: opts
 
 proc rawNewModule(g: BModuleList; module: PSym, filename: string): BModule =
   new(result)
+  result.g = g
   result.tmpBase = rope("TM" & $hashOwner(module) & "_")
   result.headerFiles = @[]
   result.declaredThings = initIntSet()
@@ -1241,14 +1241,13 @@ proc rawNewModule(g: BModuleList; module: PSym, filename: string): BModule =
   result.forwardedProcs = @[]
   result.typeNodesName = getTempName(result)
   result.nimTypesName = getTempName(result)
-  result.g = g
   # no line tracing for the init sections of the system module so that we
   # don't generate a TFrame which can confuse the stack botton initialization:
   if sfSystemModule in module.flags:
     incl result.flags, preventStackTrace
     excl(result.preInitProc.options, optStackTrace)
     excl(result.postInitProc.options, optStackTrace)
-  let ndiName = if optCDebug in gGlobalOptions: changeFileExt(completeCFilePath(filename), "ndi")
+  let ndiName = if optCDebug in g.config.globalOptions: changeFileExt(completeCFilePath(g.config, filename), "ndi")
                 else: ""
   open(result.ndi, ndiName)
 
@@ -1301,7 +1300,7 @@ proc resetCgenModules*(g: BModuleList) =
   for m in cgenModules(g): resetModule(m)
 
 proc rawNewModule(g: BModuleList; module: PSym): BModule =
-  result = rawNewModule(g, module, module.position.int32.toFullPath)
+  result = rawNewModule(g, module, module.position.FileIndex.toFullPath)
 
 proc newModule(g: BModuleList; module: PSym): BModule =
   # we should create only one cgen module for each module sym
@@ -1309,22 +1308,19 @@ proc newModule(g: BModuleList; module: PSym): BModule =
   growCache g.modules, module.position
   g.modules[module.position] = result
 
-  if (optDeadCodeElim in gGlobalOptions):
-    if (sfDeadCodeElim in module.flags):
-      internalError("added pending module twice: " & module.filename)
-
-template injectG(config) {.dirty.} =
+template injectG() {.dirty.} =
   if graph.backend == nil:
-    graph.backend = newModuleList(config)
+    graph.backend = newModuleList(graph)
   let g = BModuleList(graph.backend)
 
 proc myOpen(graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext =
-  injectG(graph.config)
+  injectG()
   result = newModule(g, module)
-  if optGenIndex in gGlobalOptions and g.generatedHeader == nil:
-    let f = if graph.config.headerFile.len > 0: graph.config.headerFile else: gProjectFull
+  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
     g.generatedHeader = rawNewModule(g, module,
-      changeFileExt(completeCFilePath(f), hExt))
+      changeFileExt(completeCFilePath(graph.config, f), hExt))
     incl g.generatedHeader.flags, isHeaderFile
 
 proc writeHeader(m: BModule) =
@@ -1335,43 +1331,44 @@ proc writeHeader(m: BModule) =
 
   var guard = "__$1__" % [m.filename.splitFile.name.rope]
   result.addf("#ifndef $1$n#define $1$n", [guard])
-  addIntTypes(result)
+  addIntTypes(result, m.config)
   generateHeaders(m)
 
   generateThreadLocalStorage(m)
   for i in countup(cfsHeaders, cfsProcs):
-    add(result, genSectionStart(i))
+    add(result, genSectionStart(i, m.config))
     add(result, m.s[i])
-    add(result, genSectionEnd(i))
-    if useNimNamespace and i == cfsHeaders: result.add openNamespaceNim()
+    add(result, genSectionEnd(i, m.config))
+    if optUseNimNamespace in m.config.globalOptions and i == cfsHeaders: result.add openNamespaceNim()
   add(result, m.s[cfsInitProc])
 
-  if optGenDynLib in gGlobalOptions:
+  if optGenDynLib in m.config.globalOptions:
     result.add("N_LIB_IMPORT ")
   result.addf("N_CDECL(void, NimMain)(void);$n", [])
-  if useNimNamespace: result.add closeNamespaceNim()
+  if optUseNimNamespace in m.config.globalOptions: result.add closeNamespaceNim()
   result.addf("#endif /* $1 */$n", [guard])
   writeRope(result, m.filename)
 
 proc getCFile(m: BModule): string =
   let ext =
       if m.compileToCpp: ".cpp"
-      elif gCmd == cmdCompileToOC or sfCompileToObjC in m.module.flags: ".m"
+      elif m.config.cmd == cmdCompileToOC or sfCompileToObjC in m.module.flags: ".m"
       else: ".c"
-  result = changeFileExt(completeCFilePath(m.cfilename.withPackageName), ext)
+  result = changeFileExt(completeCFilePath(m.config, withPackageName(m.config, m.cfilename)), ext)
 
 proc myOpenCached(graph: ModuleGraph; module: PSym, rd: PRodReader): PPassContext =
-  injectG(graph.config)
+  injectG()
   var m = newModule(g, module)
   readMergeInfo(getCFile(m), m)
   result = m
 
 proc myProcess(b: PPassContext, n: PNode): PNode =
   result = n
-  if b == nil or passes.skipCodegen(n): return
+  if b == nil: return
   var m = BModule(b)
+  if passes.skipCodegen(m.config, n): return
   m.initProc.options = initProcOptions(m)
-  softRnl = if optLineDir in gOptions: noRnl else: rnl
+  softRnl = if optLineDir in m.config.options: noRnl else: rnl
   genStmts(m.initProc, n)
 
 proc finishModule(m: BModule) =
@@ -1381,18 +1378,18 @@ proc finishModule(m: BModule) =
     # a ``for`` loop here
     var prc = m.forwardedProcs[i]
     if sfForward in prc.flags:
-      internalError(prc.info, "still forwarded: " & prc.name.s)
+      internalError(m.config, prc.info, "still forwarded: " & prc.name.s)
     genProcNoForward(m, prc)
     inc(i)
   assert(m.g.forwardedProcsCounter >= i)
   dec(m.g.forwardedProcsCounter, i)
   setLen(m.forwardedProcs, 0)
 
-proc shouldRecompile(code: Rope, cfile: Cfile): bool =
+proc shouldRecompile(m: BModule; code: Rope, cfile: Cfile): bool =
   result = true
-  if optForceFullMake notin gGlobalOptions:
+  if optForceFullMake notin m.config.globalOptions:
     if not equalsFile(code, cfile.cname):
-      if isDefined("nimdiff"):
+      if isDefined(m.config, "nimdiff"):
         if fileExists(cfile.cname):
           copyFile(cfile.cname, cfile.cname & ".backup")
           echo "diff ", cfile.cname, ".backup ", cfile.cname
@@ -1415,7 +1412,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 gGlobalOptions:
+  if m.rd == nil or optForceFullMake in m.config.globalOptions:
     genInitCode(m)
     finishTypeDescriptions(m)
     if sfMainModule in m.module.flags:
@@ -1423,35 +1420,35 @@ proc writeModule(m: BModule, pending: bool) =
       add(m.s[cfsProcHeaders], m.g.mainModProcs)
       generateThreadVarsSize(m)
 
-    var cf = Cfile(cname: cfile, obj: completeCFilePath(toObjFile(cfile)), flags: {})
+    var cf = Cfile(cname: cfile, obj: completeCFilePath(m.config, toObjFile(m.config, cfile)), flags: {})
     var code = genModule(m, cf)
     when hasTinyCBackend:
-      if gCmd == cmdRun:
+      if conf.cmd == cmdRun:
         tccgen.compileCCode($code)
         return
 
-    if not shouldRecompile(code, cf): cf.flags = {CfileFlag.Cached}
-    addFileToCompile(cf)
+    if not shouldRecompile(m, code, cf): cf.flags = {CfileFlag.Cached}
+    addFileToCompile(m.config, cf)
   elif pending and mergeRequired(m) and sfMainModule notin m.module.flags:
-    let cf = Cfile(cname: cfile, obj: completeCFilePath(toObjFile(cfile)), flags: {})
+    let cf = Cfile(cname: cfile, obj: completeCFilePath(m.config, toObjFile(m.config, cfile)), flags: {})
     mergeFiles(cfile, m)
     genInitCode(m)
     finishTypeDescriptions(m)
     var code = genModule(m, cf)
     writeRope(code, cfile)
-    addFileToCompile(cf)
+    addFileToCompile(m.config, cf)
   else:
     # Consider: first compilation compiles ``system.nim`` and produces
     # ``system.c`` but then compilation fails due to an error. This means
     # that ``system.o`` is missing, so we need to call the C compiler for it:
-    var cf = Cfile(cname: cfile, obj: completeCFilePath(toObjFile(cfile)), flags: {})
+    var cf = Cfile(cname: cfile, obj: completeCFilePath(m.config, toObjFile(m.config, cfile)), flags: {})
     if not existsFile(cf.obj): cf.flags = {CfileFlag.Cached}
-    addFileToCompile(cf)
+    addFileToCompile(m.config, cf)
   close(m.ndi)
 
 proc updateCachedModule(m: BModule) =
   let cfile = getCFile(m)
-  var cf = Cfile(cname: cfile, obj: completeCFilePath(toObjFile(cfile)), flags: {})
+  var cf = Cfile(cname: cfile, obj: completeCFilePath(m.config, toObjFile(m.config, cfile)), flags: {})
 
   if mergeRequired(m) and sfMainModule notin m.module.flags:
     mergeFiles(cfile, m)
@@ -1462,12 +1459,13 @@ proc updateCachedModule(m: BModule) =
     writeRope(code, cfile)
   else:
     cf.flags = {CfileFlag.Cached}
-  addFileToCompile(cf)
+  addFileToCompile(m.config, cf)
 
 proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode =
   result = n
-  if b == nil or passes.skipCodegen(n): return
+  if b == nil: return
   var m = BModule(b)
+  if passes.skipCodegen(m.config, n): return
   # 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?
@@ -1501,7 +1499,7 @@ proc cgenWriteModules*(backend: RootRef, config: ConfigRef) =
       m.updateCachedModule
     else:
       m.writeModule(pending=true)
-  writeMapping(g.mapping)
+  writeMapping(config, g.mapping)
   if g.generatedHeader != nil: writeHeader(g.generatedHeader)
 
 const cgenPass* = makePass(myOpen, myOpenCached, myProcess, myClose)
diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim
index f8167acdc..ce3fc2f90 100644
--- a/compiler/cgendata.nim
+++ b/compiler/cgendata.nim
@@ -14,6 +14,7 @@ import
   tables, ndi
 
 from msgs import TLineInfo
+from modulegraphs import ModuleGraph
 
 type
   TLabel* = Rope              # for the C generator a label is just a rope
@@ -116,6 +117,7 @@ type
     breakpoints*: Rope # later the breakpoints are inserted into the main proc
     typeInfoMarker*: TypeCache
     config*: ConfigRef
+    graph*: ModuleGraph
     strVersion*, seqVersion*: int # version of the string/seq implementation to use
 
   TCGen = object of TPassContext # represents a C source file
@@ -148,6 +150,9 @@ type
     g*: BModuleList
     ndi*: NdiFile
 
+template config*(m: BModule): ConfigRef = m.g.config
+template config*(p: BProc): ConfigRef = p.module.g.config
+
 proc includeHeader*(this: BModule; header: string) =
   if not this.headerFiles.contains header:
     this.headerFiles.add header
@@ -165,14 +170,15 @@ proc newProc*(prc: PSym, module: BModule): BProc =
   result.prc = prc
   result.module = module
   if prc != nil: result.options = prc.options
-  else: result.options = gOptions
+  else: result.options = module.config.options
   newSeq(result.blocks, 1)
   result.nestedTryStmts = @[]
   result.finallySafePoints = @[]
   result.sigConflicts = initCountTable[string]()
 
-proc newModuleList*(config: ConfigRef): BModuleList =
-  BModuleList(modules: @[], typeInfoMarker: initTable[SigHash, Rope](), config: config)
+proc newModuleList*(g: ModuleGraph): BModuleList =
+  BModuleList(modules: @[], typeInfoMarker: initTable[SigHash, Rope](), config: g.config,
+    graph: g)
 
 iterator cgenModules*(g: BModuleList): BModule =
   for i in 0..high(g.modules):
diff --git a/compiler/cgmeth.nim b/compiler/cgmeth.nim
index 0513e88f4..1d72952e2 100644
--- a/compiler/cgmeth.nim
+++ b/compiler/cgmeth.nim
@@ -11,9 +11,9 @@
 
 import
   intsets, options, ast, astalgo, msgs, idents, renderer, types, magicsys,
-  sempass2, strutils, modulegraphs
+  sempass2, strutils, modulegraphs, configuration
 
-proc genConv(n: PNode, d: PType, downcast: bool): PNode =
+proc genConv(n: PNode, d: PType, downcast: bool; conf: ConfigRef): PNode =
   var dest = skipTypes(d, abstractPtrs)
   var source = skipTypes(n.typ, abstractPtrs)
   if (source.kind == tyObject) and (dest.kind == tyObject):
@@ -24,12 +24,12 @@ proc genConv(n: PNode, d: PType, downcast: bool): PNode =
     elif diff < 0:
       result = newNodeIT(nkObjUpConv, n.info, d)
       addSon(result, n)
-      if downcast: internalError(n.info, "cgmeth.genConv: no upcast allowed")
+      if downcast: internalError(conf, n.info, "cgmeth.genConv: no upcast allowed")
     elif diff > 0:
       result = newNodeIT(nkObjDownConv, n.info, d)
       addSon(result, n)
       if not downcast:
-        internalError(n.info, "cgmeth.genConv: no downcast allowed")
+        internalError(conf, n.info, "cgmeth.genConv: no downcast allowed")
     else:
       result = n
   else:
@@ -42,7 +42,7 @@ proc getDispatcher*(s: PSym): PSym =
     let disp = dispn.sym
     if sfDispatcher in disp.flags: result = disp
 
-proc methodCall*(n: PNode): PNode =
+proc methodCall*(n: PNode; conf: ConfigRef): PNode =
   result = n
   # replace ordinary method by dispatcher method:
   let disp = getDispatcher(result.sons[0].sym)
@@ -50,9 +50,9 @@ proc methodCall*(n: PNode): PNode =
     result.sons[0].sym = disp
     # change the arguments to up/downcasts to fit the dispatcher's parameters:
     for i in countup(1, sonsLen(result)-1):
-      result.sons[i] = genConv(result.sons[i], disp.typ.sons[i], true)
+      result.sons[i] = genConv(result.sons[i], disp.typ.sons[i], true, conf)
   else:
-    localError(n.info, "'" & $result.sons[0] & "' lacks a dispatcher")
+    localError(conf, n.info, "'" & $result.sons[0] & "' lacks a dispatcher")
 
 type
   MethodResult = enum No, Invalid, Yes
@@ -130,7 +130,7 @@ proc createDispatcher(s: PSym): PSym =
   attachDispatcher(disp, newSymNode(disp))
   return disp
 
-proc fixupDispatcher(meth, disp: PSym) =
+proc fixupDispatcher(meth, disp: PSym; conf: ConfigRef) =
   # We may have constructed the dispatcher from a method prototype
   # and need to augment the incomplete dispatcher with information
   # from later definitions, particularly the resultPos slot. Also,
@@ -149,7 +149,7 @@ proc fixupDispatcher(meth, disp: PSym) =
       disp.typ.lockLevel = meth.typ.lockLevel
     elif meth.typ.lockLevel != UnspecifiedLockLevel and
          meth.typ.lockLevel != disp.typ.lockLevel:
-      message(meth.info, warnLockLevel,
+      message(conf, meth.info, warnLockLevel,
         "method has lock level $1, but another method has $2" %
         [$meth.typ.lockLevel, $disp.typ.lockLevel])
       # XXX The following code silences a duplicate warning in
@@ -166,13 +166,13 @@ proc methodDef*(g: ModuleGraph; s: PSym, fromCache: bool) =
     of Yes:
       add(g.methods[i].methods, s)
       attachDispatcher(s, lastSon(disp.ast))
-      fixupDispatcher(s, disp)
+      fixupDispatcher(s, disp, g.config)
       #echo "fixup ", disp.name.s, " ", disp.id
-      when useEffectSystem: checkMethodEffects(disp, s)
+      when useEffectSystem: checkMethodEffects(g, disp, s)
       if {sfBase, sfFromGeneric} * s.flags == {sfBase} and
            g.methods[i].methods[0] != s:
         # already exists due to forwarding definition?
-        localError(s.info, "method is not a base")
+        localError(g.config, s.info, "method is not a base")
       return
     of No: discard
     of Invalid:
@@ -183,10 +183,10 @@ proc methodDef*(g: ModuleGraph; s: PSym, fromCache: bool) =
   #if fromCache:
   #  internalError(s.info, "no method dispatcher found")
   if witness != nil:
-    localError(s.info, "invalid declaration order; cannot attach '" & s.name.s &
+    localError(g.config, s.info, "invalid declaration order; cannot attach '" & s.name.s &
                        "' to method defined here: " & $witness.info)
   elif sfBase notin s.flags:
-    message(s.info, warnUseBase)
+    message(g.config, s.info, warnUseBase)
 
 proc relevantCol(methods: TSymSeq, col: int): bool =
   # returns true iff the position is relevant
@@ -225,32 +225,33 @@ proc sortBucket(a: var TSymSeq, relevantCols: IntSet) =
       a[j] = v
     if h == 1: break
 
-proc genDispatcher(methods: TSymSeq, relevantCols: IntSet): PSym =
+proc genDispatcher(g: ModuleGraph; methods: TSymSeq, relevantCols: IntSet): PSym =
   var base = lastSon(methods[0].ast).sym
   result = base
   var paramLen = sonsLen(base.typ)
   var nilchecks = newNodeI(nkStmtList, base.info)
   var disp = newNodeI(nkIfStmt, base.info)
-  var ands = getSysSym("and")
-  var iss = getSysSym("of")
+  var ands = getSysSym(g, unknownLineInfo(), "and")
+  var iss = getSysSym(g, unknownLineInfo(), "of")
+  let boolType = getSysType(g, unknownLineInfo(), tyBool)
   for col in countup(1, paramLen - 1):
     if contains(relevantCols, col):
       let param = base.typ.n.sons[col].sym
       if param.typ.skipTypes(abstractInst).kind in {tyRef, tyPtr}:
         addSon(nilchecks, newTree(nkCall,
-            newSymNode(getCompilerProc"chckNilDisp"), newSymNode(param)))
+            newSymNode(getCompilerProc(g, "chckNilDisp")), newSymNode(param)))
   for meth in countup(0, high(methods)):
     var curr = methods[meth]      # generate condition:
     var cond: PNode = nil
     for col in countup(1, paramLen - 1):
       if contains(relevantCols, col):
-        var isn = newNodeIT(nkCall, base.info, getSysType(tyBool))
+        var isn = newNodeIT(nkCall, base.info, boolType)
         addSon(isn, newSymNode(iss))
         let param = base.typ.n.sons[col].sym
         addSon(isn, newSymNode(param))
         addSon(isn, newNodeIT(nkType, base.info, curr.typ.sons[col]))
         if cond != nil:
-          var a = newNodeIT(nkCall, base.info, getSysType(tyBool))
+          var a = newNodeIT(nkCall, base.info, boolType)
           addSon(a, newSymNode(ands))
           addSon(a, cond)
           addSon(a, isn)
@@ -262,7 +263,7 @@ proc genDispatcher(methods: TSymSeq, relevantCols: IntSet): PSym =
     addSon(call, newSymNode(curr))
     for col in countup(1, paramLen - 1):
       addSon(call, genConv(newSymNode(base.typ.n.sons[col].sym),
-                           curr.typ.sons[col], false))
+                           curr.typ.sons[col], false, g.config))
     var ret: PNode
     if retTyp != nil:
       var a = newNodeI(nkFastAsgn, base.info)
@@ -290,4 +291,4 @@ proc generateMethodDispatchers*(g: ModuleGraph): PNode =
       if relevantCol(g.methods[bucket].methods, col): incl(relevantCols, col)
     sortBucket(g.methods[bucket].methods, relevantCols)
     addSon(result,
-           newSymNode(genDispatcher(g.methods[bucket].methods, relevantCols)))
+           newSymNode(genDispatcher(g, g.methods[bucket].methods, relevantCols)))
diff --git a/compiler/commands.nim b/compiler/commands.nim
index 820cb7e1a..09f63f0f5 100644
--- a/compiler/commands.nim
+++ b/compiler/commands.nim
@@ -18,7 +18,6 @@ template bootSwitch(name, expr, userString) =
 
 bootSwitch(usedRelease, defined(release), "-d:release")
 bootSwitch(usedGnuReadline, defined(useLinenoise), "-d:useLinenoise")
-bootSwitch(usedNoCaas, defined(noCaas), "-d:noCaas")
 bootSwitch(usedBoehm, defined(boehmgc), "--gc:boehm")
 bootSwitch(usedMarkAndSweep, defined(gcmarkandsweep), "--gc:markAndSweep")
 bootSwitch(usedGenerational, defined(gcgenerational), "--gc:generational")
@@ -27,90 +26,100 @@ bootSwitch(usedNoGC, defined(nogc), "--gc:none")
 
 import
   os, msgs, options, nversion, condsyms, strutils, extccomp, platform,
-  wordrecg, parseutils, nimblecmd, idents, parseopt, sequtils
+  wordrecg, parseutils, nimblecmd, idents, parseopt, sequtils, configuration
 
 # but some have deps to imported modules. Yay.
 bootSwitch(usedTinyC, hasTinyCBackend, "-d:tinyc")
-bootSwitch(usedAvoidTimeMachine, noTimeMachine, "-d:avoidTimeMachine")
 bootSwitch(usedNativeStacktrace,
   defined(nativeStackTrace) and nativeStackTraceSupported,
   "-d:nativeStackTrace")
 bootSwitch(usedFFI, hasFFI, "-d:useFFI")
 
-
-proc writeCommandLineUsage*()
-
 type
   TCmdLinePass* = enum
     passCmd1,                 # first pass over the command line
     passCmd2,                 # second pass over the command line
     passPP                    # preprocessor called processCommand()
 
-proc processCommand*(switch: string, pass: TCmdLinePass)
-proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
-                    config: ConfigRef = nil)
-
-# implementation
-
 const
   HelpMessage = "Nim Compiler Version $1 [$2: $3]\n" &
+      "Compiled at $4\n" &
       "Copyright (c) 2006-" & copyrightYear & " by Andreas Rumpf\n"
 
 const
   Usage = slurp"../doc/basicopt.txt".replace("//", "")
-  AdvancedUsage = slurp"../doc/advopt.txt".replace("//", "")
+  FeatureDesc = block:
+    var x = ""
+    for f in low(Feature)..high(Feature):
+      if x.len > 0: x.add "|"
+      x.add $f
+    x
+  AdvancedUsage = slurp"../doc/advopt.txt".replace("//", "") % FeatureDesc
 
 proc getCommandLineDesc(): string =
   result = (HelpMessage % [VersionAsString, platform.OS[platform.hostOS].name,
-                           CPU[platform.hostCPU].name]) & Usage
+                           CPU[platform.hostCPU].name, CompileDate]) &
+                           Usage
 
-proc helpOnError(pass: TCmdLinePass) =
+proc helpOnError(conf: ConfigRef; pass: TCmdLinePass) =
   if pass == passCmd1:
-    msgWriteln(getCommandLineDesc(), {msgStdout})
+    msgWriteln(conf, getCommandLineDesc(), {msgStdout})
     msgQuit(0)
 
-proc writeAdvancedUsage(pass: TCmdLinePass) =
+proc writeAdvancedUsage(conf: ConfigRef; pass: TCmdLinePass) =
   if pass == passCmd1:
-    msgWriteln(`%`(HelpMessage, [VersionAsString,
+    msgWriteln(conf, (HelpMessage % [VersionAsString,
                                  platform.OS[platform.hostOS].name,
-                                 CPU[platform.hostCPU].name]) & AdvancedUsage,
+                                 CPU[platform.hostCPU].name, CompileDate]) &
+                                 AdvancedUsage,
                {msgStdout})
     msgQuit(0)
 
-proc writeVersionInfo(pass: TCmdLinePass) =
+proc writeFullhelp(conf: ConfigRef; pass: TCmdLinePass) =
   if pass == passCmd1:
-    msgWriteln(`%`(HelpMessage, [VersionAsString,
+    msgWriteln(conf, `%`(HelpMessage, [VersionAsString,
                                  platform.OS[platform.hostOS].name,
-                                 CPU[platform.hostCPU].name]),
+                                 CPU[platform.hostCPU].name, CompileDate]) &
+                                 Usage & AdvancedUsage,
+               {msgStdout})
+    msgQuit(0)
+
+proc writeVersionInfo(conf: ConfigRef; pass: TCmdLinePass) =
+  if pass == passCmd1:
+    msgWriteln(conf, `%`(HelpMessage, [VersionAsString,
+                                 platform.OS[platform.hostOS].name,
+                                 CPU[platform.hostCPU].name, CompileDate]),
                {msgStdout})
 
     const gitHash = gorge("git log -n 1 --format=%H").strip
     when gitHash.len == 40:
-      msgWriteln("git hash: " & gitHash, {msgStdout})
+      msgWriteln(conf, "git hash: " & gitHash, {msgStdout})
 
-    msgWriteln("active boot switches:" & usedRelease & usedAvoidTimeMachine &
-      usedTinyC & usedGnuReadline & usedNativeStacktrace & usedNoCaas &
+    msgWriteln(conf, "active boot switches:" & usedRelease &
+      usedTinyC & usedGnuReadline & usedNativeStacktrace &
       usedFFI & usedBoehm & usedMarkAndSweep & usedGenerational & usedGoGC & usedNoGC,
                {msgStdout})
     msgQuit(0)
 
-var
-  helpWritten: bool
-
-proc writeCommandLineUsage() =
+proc writeCommandLineUsage*(conf: ConfigRef; helpWritten: var bool) =
   if not helpWritten:
-    msgWriteln(getCommandLineDesc(), {msgStdout})
+    msgWriteln(conf, getCommandLineDesc(), {msgStdout})
     helpWritten = true
 
 proc addPrefix(switch: string): string =
   if len(switch) == 1: result = "-" & switch
   else: result = "--" & switch
 
-proc invalidCmdLineOption(pass: TCmdLinePass, switch: string, info: TLineInfo) =
-  if switch == " ": localError(info, errInvalidCmdLineOption, "-")
-  else: localError(info, errInvalidCmdLineOption, addPrefix(switch))
+const
+  errInvalidCmdLineOption = "invalid command line option: '$1'"
+  errOnOrOffExpectedButXFound = "'on' or 'off' expected, but '$1' found"
+  errOnOffOrListExpectedButXFound = "'on', 'off' or 'list' expected, but '$1' found"
+
+proc invalidCmdLineOption(conf: ConfigRef; pass: TCmdLinePass, switch: string, info: TLineInfo) =
+  if switch == " ": localError(conf, info, errInvalidCmdLineOption % "-")
+  else: localError(conf, info, errInvalidCmdLineOption % addPrefix(switch))
 
-proc splitSwitch(switch: string, cmd, arg: var string, pass: TCmdLinePass,
+proc splitSwitch(conf: ConfigRef; switch: string, cmd, arg: var string, pass: TCmdLinePass,
                  info: TLineInfo) =
   cmd = ""
   var i = 0
@@ -123,46 +132,41 @@ proc splitSwitch(switch: string, cmd, arg: var string, pass: TCmdLinePass,
     inc(i)
   if i >= len(switch): arg = ""
   elif switch[i] in {':', '=', '['}: arg = substr(switch, i + 1)
-  else: invalidCmdLineOption(pass, switch, info)
+  else: invalidCmdLineOption(conf, pass, switch, info)
 
-proc processOnOffSwitch(op: TOptions, arg: string, pass: TCmdLinePass,
+proc processOnOffSwitch(conf: ConfigRef; op: TOptions, arg: string, pass: TCmdLinePass,
                         info: TLineInfo) =
   case arg.normalize
-  of "on": gOptions = gOptions + op
-  of "off": gOptions = gOptions - op
-  else: localError(info, errOnOrOffExpectedButXFound, arg)
+  of "on": conf.options = conf.options + op
+  of "off": conf.options = conf.options - op
+  else: localError(conf, info, errOnOrOffExpectedButXFound % arg)
 
-proc processOnOffSwitchOrList(op: TOptions, arg: string, pass: TCmdLinePass,
+proc processOnOffSwitchOrList(conf: ConfigRef; op: TOptions, arg: string, pass: TCmdLinePass,
                               info: TLineInfo): bool =
   result = false
   case arg.normalize
-  of "on": gOptions = gOptions + op
-  of "off": gOptions = gOptions - op
-  else:
-    if arg == "list":
-      result = true
-    else:
-      localError(info, errOnOffOrListExpectedButXFound, arg)
+  of "on": conf.options = conf.options + op
+  of "off": conf.options = conf.options - op
+  of "list": result = true
+  else: localError(conf, info, errOnOffOrListExpectedButXFound % arg)
 
-proc processOnOffSwitchG(op: TGlobalOptions, arg: string, pass: TCmdLinePass,
+proc processOnOffSwitchG(conf: ConfigRef; op: TGlobalOptions, arg: string, pass: TCmdLinePass,
                          info: TLineInfo) =
   case arg.normalize
-  of "on": gGlobalOptions = gGlobalOptions + op
-  of "off": gGlobalOptions = gGlobalOptions - op
-  else: localError(info, errOnOrOffExpectedButXFound, arg)
+  of "on": conf.globalOptions = conf.globalOptions + op
+  of "off": conf.globalOptions = conf.globalOptions - op
+  else: localError(conf, info, errOnOrOffExpectedButXFound % arg)
 
-proc expectArg(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
-  if arg == "": localError(info, errCmdLineArgExpected, addPrefix(switch))
+proc expectArg(conf: ConfigRef; switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
+  if arg == "":
+    localError(conf, info, "argument for command line option expected: '$1'" % addPrefix(switch))
 
-proc expectNoArg(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
-  if arg != "": localError(info, errCmdLineNoArgExpected, addPrefix(switch))
-
-var
-  enableNotes: TNoteKinds
-  disableNotes: TNoteKinds
+proc expectNoArg(conf: ConfigRef; switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
+  if arg != "":
+    localError(conf, info, "invalid argument for command line option: '$1'" % addPrefix(switch))
 
 proc processSpecificNote*(arg: string, state: TSpecialWord, pass: TCmdLinePass,
-                         info: TLineInfo; orig: string) =
+                         info: TLineInfo; orig: string; conf: ConfigRef) =
   var id = ""  # arg = "X]:on|off"
   var i = 0
   var n = hintMin
@@ -170,124 +174,127 @@ proc processSpecificNote*(arg: string, state: TSpecialWord, pass: TCmdLinePass,
     add(id, arg[i])
     inc(i)
   if i < len(arg) and (arg[i] == ']'): inc(i)
-  else: invalidCmdLineOption(pass, orig, info)
+  else: invalidCmdLineOption(conf, pass, orig, info)
   if i < len(arg) and (arg[i] in {':', '='}): inc(i)
-  else: invalidCmdLineOption(pass, orig, info)
+  else: invalidCmdLineOption(conf, pass, orig, info)
   if state == wHint:
-    var x = findStr(msgs.HintsToStr, id)
+    let x = findStr(configuration.HintsToStr, id)
     if x >= 0: n = TNoteKind(x + ord(hintMin))
-    else: localError(info, "unknown hint: " & id)
+    else: localError(conf, info, "unknown hint: " & id)
   else:
-    var x = findStr(msgs.WarningsToStr, id)
+    let x = findStr(configuration.WarningsToStr, id)
     if x >= 0: n = TNoteKind(x + ord(warnMin))
-    else: localError(info, "unknown warning: " & id)
+    else: localError(conf, info, "unknown warning: " & id)
   case substr(arg, i).normalize
   of "on":
-    incl(gNotes, n)
-    incl(gMainPackageNotes, n)
-    incl(enableNotes, n)
+    incl(conf.notes, n)
+    incl(conf.mainPackageNotes, n)
+    incl(conf.enableNotes, n)
   of "off":
-    excl(gNotes, n)
-    excl(gMainPackageNotes, n)
-    incl(disableNotes, n)
-    excl(ForeignPackageNotes, n)
-  else: localError(info, errOnOrOffExpectedButXFound, arg)
-
-proc processCompile(filename: string) =
-  var found = findFile(filename)
+    excl(conf.notes, n)
+    excl(conf.mainPackageNotes, n)
+    incl(conf.disableNotes, n)
+    excl(conf.foreignPackageNotes, n)
+  else: localError(conf, info, errOnOrOffExpectedButXFound % arg)
+
+proc processCompile(conf: ConfigRef; filename: string) =
+  var found = findFile(conf, filename)
   if found == "": found = filename
-  extccomp.addExternalFileToCompile(found)
+  extccomp.addExternalFileToCompile(conf, found)
+
+const
+  errNoneBoehmRefcExpectedButXFound = "'none', 'boehm' or 'refc' expected, but '$1' found"
+  errNoneSpeedOrSizeExpectedButXFound = "'none', 'speed' or 'size' expected, but '$1' found"
+  errGuiConsoleOrLibExpectedButXFound = "'gui', 'console' or 'lib' expected, but '$1' found"
 
-proc testCompileOptionArg*(switch, arg: string, info: TLineInfo): bool =
+proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo): bool =
   case switch.normalize
   of "gc":
     case arg.normalize
-    of "boehm":        result = gSelectedGC == gcBoehm
-    of "refc":         result = gSelectedGC == gcRefc
-    of "v2":           result = gSelectedGC == gcV2
-    of "markandsweep": result = gSelectedGC == gcMarkAndSweep
-    of "generational": result = gSelectedGC == gcGenerational
-    of "go":           result = gSelectedGC == gcGo
-    of "none":         result = gSelectedGC == gcNone
-    of "stack", "regions": result = gSelectedGC == gcRegions
-    else: localError(info, errNoneBoehmRefcExpectedButXFound, arg)
+    of "boehm":        result = conf.selectedGC == gcBoehm
+    of "refc":         result = conf.selectedGC == gcRefc
+    of "v2":           result = conf.selectedGC == gcV2
+    of "markandsweep": result = conf.selectedGC == gcMarkAndSweep
+    of "generational": result = conf.selectedGC == gcGenerational
+    of "go":           result = conf.selectedGC == gcGo
+    of "none":         result = conf.selectedGC == gcNone
+    of "stack", "regions": result = conf.selectedGC == gcRegions
+    else: localError(conf, info, errNoneBoehmRefcExpectedButXFound % arg)
   of "opt":
     case arg.normalize
-    of "speed": result = contains(gOptions, optOptimizeSpeed)
-    of "size": result = contains(gOptions, optOptimizeSize)
-    of "none": result = gOptions * {optOptimizeSpeed, optOptimizeSize} == {}
-    else: localError(info, errNoneSpeedOrSizeExpectedButXFound, arg)
-  of "verbosity": result = $gVerbosity == arg
+    of "speed": result = contains(conf.options, optOptimizeSpeed)
+    of "size": result = contains(conf.options, optOptimizeSize)
+    of "none": result = conf.options * {optOptimizeSpeed, optOptimizeSize} == {}
+    else: localError(conf, info, errNoneSpeedOrSizeExpectedButXFound % arg)
+  of "verbosity": result = $conf.verbosity == arg
   of "app":
     case arg.normalize
-    of "gui":       result = contains(gGlobalOptions, optGenGuiApp)
-    of "console":   result = not contains(gGlobalOptions, optGenGuiApp)
-    of "lib":       result = contains(gGlobalOptions, optGenDynLib) and
-                      not contains(gGlobalOptions, optGenGuiApp)
-    of "staticlib": result = contains(gGlobalOptions, optGenStaticLib) and
-                      not contains(gGlobalOptions, optGenGuiApp)
-    else: localError(info, errGuiConsoleOrLibExpectedButXFound, arg)
+    of "gui":       result = contains(conf.globalOptions, optGenGuiApp)
+    of "console":   result = not contains(conf.globalOptions, optGenGuiApp)
+    of "lib":       result = contains(conf.globalOptions, optGenDynLib) and
+                      not contains(conf.globalOptions, optGenGuiApp)
+    of "staticlib": result = contains(conf.globalOptions, optGenStaticLib) and
+                      not contains(conf.globalOptions, optGenGuiApp)
+    else: localError(conf, info, errGuiConsoleOrLibExpectedButXFound % arg)
   of "dynliboverride":
-    result = isDynlibOverride(arg)
-  else: invalidCmdLineOption(passCmd1, switch, info)
+    result = isDynlibOverride(conf, arg)
+  else: invalidCmdLineOption(conf, passCmd1, switch, info)
 
-proc testCompileOption*(switch: string, info: TLineInfo): bool =
+proc testCompileOption*(conf: ConfigRef; switch: string, info: TLineInfo): bool =
   case switch.normalize
-  of "debuginfo": result = contains(gGlobalOptions, optCDebug)
-  of "compileonly", "c": result = contains(gGlobalOptions, optCompileOnly)
-  of "nolinking": result = contains(gGlobalOptions, optNoLinking)
-  of "nomain": result = contains(gGlobalOptions, optNoMain)
-  of "forcebuild", "f": result = contains(gGlobalOptions, optForceFullMake)
-  of "warnings", "w": result = contains(gOptions, optWarns)
-  of "hints": result = contains(gOptions, optHints)
-  of "threadanalysis": result = contains(gGlobalOptions, optThreadAnalysis)
-  of "stacktrace": result = contains(gOptions, optStackTrace)
-  of "linetrace": result = contains(gOptions, optLineTrace)
-  of "debugger": result = contains(gOptions, optEndb)
-  of "profiler": result = contains(gOptions, optProfiler)
-  of "memtracker": result = contains(gOptions, optMemTracker)
-  of "checks", "x": result = gOptions * ChecksOptions == ChecksOptions
+  of "debuginfo": result = contains(conf.globalOptions, optCDebug)
+  of "compileonly", "c": result = contains(conf.globalOptions, optCompileOnly)
+  of "nolinking": result = contains(conf.globalOptions, optNoLinking)
+  of "nomain": result = contains(conf.globalOptions, optNoMain)
+  of "forcebuild", "f": result = contains(conf.globalOptions, optForceFullMake)
+  of "warnings", "w": result = contains(conf.options, optWarns)
+  of "hints": result = contains(conf.options, optHints)
+  of "threadanalysis": result = contains(conf.globalOptions, optThreadAnalysis)
+  of "stacktrace": result = contains(conf.options, optStackTrace)
+  of "linetrace": result = contains(conf.options, optLineTrace)
+  of "debugger": result = contains(conf.options, optEndb)
+  of "profiler": result = contains(conf.options, optProfiler)
+  of "memtracker": result = contains(conf.options, optMemTracker)
+  of "checks", "x": result = conf.options * ChecksOptions == ChecksOptions
   of "floatchecks":
-    result = gOptions * {optNaNCheck, optInfCheck} == {optNaNCheck, optInfCheck}
-  of "infchecks": result = contains(gOptions, optInfCheck)
-  of "nanchecks": result = contains(gOptions, optNaNCheck)
-  of "nilchecks": result = contains(gOptions, optNilCheck)
-  of "objchecks": result = contains(gOptions, optObjCheck)
-  of "fieldchecks": result = contains(gOptions, optFieldCheck)
-  of "rangechecks": result = contains(gOptions, optRangeCheck)
-  of "boundchecks": result = contains(gOptions, optBoundsCheck)
-  of "overflowchecks": result = contains(gOptions, optOverflowCheck)
-  of "movechecks": result = contains(gOptions, optMoveCheck)
-  of "linedir": result = contains(gOptions, optLineDir)
-  of "assertions", "a": result = contains(gOptions, optAssert)
-  of "deadcodeelim": result = contains(gGlobalOptions, optDeadCodeElim)
-  of "run", "r": result = contains(gGlobalOptions, optRun)
-  of "symbolfiles": result = gSymbolFiles != disabledSf
-  of "genscript": result = contains(gGlobalOptions, optGenScript)
-  of "threads": result = contains(gGlobalOptions, optThreads)
-  of "taintmode": result = contains(gGlobalOptions, optTaintMode)
-  of "tlsemulation": result = contains(gGlobalOptions, optTlsEmulation)
-  of "implicitstatic": result = contains(gOptions, optImplicitStatic)
-  of "patterns": result = contains(gOptions, optPatterns)
-  of "experimental": result = gExperimentalMode
-  of "excessivestacktrace": result = contains(gGlobalOptions, optExcessiveStackTrace)
-  else: invalidCmdLineOption(passCmd1, switch, info)
-
-proc processPath(path: string, info: TLineInfo,
+    result = conf.options * {optNaNCheck, optInfCheck} == {optNaNCheck, optInfCheck}
+  of "infchecks": result = contains(conf.options, optInfCheck)
+  of "nanchecks": result = contains(conf.options, optNaNCheck)
+  of "nilchecks": result = contains(conf.options, optNilCheck)
+  of "objchecks": result = contains(conf.options, optObjCheck)
+  of "fieldchecks": result = contains(conf.options, optFieldCheck)
+  of "rangechecks": result = contains(conf.options, optRangeCheck)
+  of "boundchecks": result = contains(conf.options, optBoundsCheck)
+  of "overflowchecks": result = contains(conf.options, optOverflowCheck)
+  of "movechecks": result = contains(conf.options, optMoveCheck)
+  of "linedir": result = contains(conf.options, optLineDir)
+  of "assertions", "a": result = contains(conf.options, optAssert)
+  of "run", "r": result = contains(conf.globalOptions, optRun)
+  of "symbolfiles": result = conf.symbolFiles != disabledSf
+  of "genscript": result = contains(conf.globalOptions, optGenScript)
+  of "threads": result = contains(conf.globalOptions, optThreads)
+  of "taintmode": result = contains(conf.globalOptions, optTaintMode)
+  of "tlsemulation": result = contains(conf.globalOptions, optTlsEmulation)
+  of "implicitstatic": result = contains(conf.options, optImplicitStatic)
+  of "patterns": result = contains(conf.options, optPatterns)
+  of "excessivestacktrace": result = contains(conf.globalOptions, optExcessiveStackTrace)
+  else: invalidCmdLineOption(conf, passCmd1, switch, info)
+
+proc processPath(conf: ConfigRef; path: string, info: TLineInfo,
                  notRelativeToProj = false): string =
   let p = if os.isAbsolute(path) or '$' in path:
             path
           elif notRelativeToProj:
             getCurrentDir() / path
           else:
-            options.gProjectPath / path
+            conf.projectPath / path
   try:
-    result = pathSubs(p, info.toFullPath().splitFile().dir)
+    result = pathSubs(conf, p, info.toFullPath().splitFile().dir)
   except ValueError:
-    localError(info, "invalid path: " & p)
+    localError(conf, info, "invalid path: " & p)
     result = p
 
-proc processCfgPath(path: string, info: TLineInfo): string =
+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 p = if os.isAbsolute(path) or '$' in path:
@@ -295,456 +302,466 @@ proc processCfgPath(path: string, info: TLineInfo): string =
           else:
             basedir / path
   try:
-    result = pathSubs(p, basedir)
+    result = pathSubs(conf, p, basedir)
   except ValueError:
-    localError(info, "invalid path: " & p)
+    localError(conf, info, "invalid path: " & p)
     result = p
 
-proc trackDirty(arg: string, info: TLineInfo) =
+const
+  errInvalidNumber = "$1 is not a valid number"
+
+proc trackDirty(conf: ConfigRef; arg: string, info: TLineInfo) =
   var a = arg.split(',')
-  if a.len != 4: localError(info, errTokenExpected,
-                            "DIRTY_BUFFER,ORIGINAL_FILE,LINE,COLUMN")
+  if a.len != 4: localError(conf, info,
+                            "DIRTY_BUFFER,ORIGINAL_FILE,LINE,COLUMN expected")
   var line, column: int
   if parseUtils.parseInt(a[2], line) <= 0:
-    localError(info, errInvalidNumber, a[1])
+    localError(conf, info, errInvalidNumber % a[1])
   if parseUtils.parseInt(a[3], column) <= 0:
-    localError(info, errInvalidNumber, a[2])
+    localError(conf, info, errInvalidNumber % a[2])
 
-  let dirtyOriginalIdx = a[1].fileInfoIdx
-  if dirtyOriginalIdx >= 0:
+  let dirtyOriginalIdx = fileInfoIdx(conf, a[1])
+  if dirtyOriginalIdx.int32 >= 0:
     msgs.setDirtyFile(dirtyOriginalIdx, a[0])
 
   gTrackPos = newLineInfo(dirtyOriginalIdx, line, column)
 
-proc track(arg: string, info: TLineInfo) =
+proc track(conf: ConfigRef; arg: string, info: TLineInfo) =
   var a = arg.split(',')
-  if a.len != 3: localError(info, errTokenExpected, "FILE,LINE,COLUMN")
+  if a.len != 3: localError(conf, info, "FILE,LINE,COLUMN expected")
   var line, column: int
   if parseUtils.parseInt(a[1], line) <= 0:
-    localError(info, errInvalidNumber, a[1])
+    localError(conf, info, errInvalidNumber % a[1])
   if parseUtils.parseInt(a[2], column) <= 0:
-    localError(info, errInvalidNumber, a[2])
-  gTrackPos = newLineInfo(a[0], line, column)
+    localError(conf, info, errInvalidNumber % a[2])
+  gTrackPos = newLineInfo(conf, a[0], line, column)
 
-proc dynlibOverride(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
+proc dynlibOverride(conf: ConfigRef; switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
   if pass in {passCmd2, passPP}:
-    expectArg(switch, arg, pass, info)
-    options.inclDynlibOverride(arg)
+    expectArg(conf, switch, arg, pass, info)
+    options.inclDynlibOverride(conf, arg)
 
-proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
-                   config: ConfigRef = nil) =
+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":
-    expectArg(switch, arg, pass, info)
-    addPath(if pass == passPP: processCfgPath(arg, info) else: processPath(arg, info), info)
+    expectArg(conf, switch, arg, pass, info)
+    addPath(conf, if pass == passPP: processCfgPath(conf, arg, info) else: processPath(conf, arg, info), info)
   of "nimblepath", "babelpath":
     # keep the old name for compat
-    if pass in {passCmd2, passPP} and not options.gNoNimblePath:
-      expectArg(switch, arg, pass, info)
-      var path = processPath(arg, info, notRelativeToProj=true)
+    if pass in {passCmd2, passPP} and optNoNimblePath notin conf.globalOptions:
+      expectArg(conf, switch, arg, pass, info)
+      var path = processPath(conf, arg, info, notRelativeToProj=true)
       let nimbleDir = getEnv("NIMBLE_DIR")
       if nimbleDir.len > 0 and pass == passPP: path = nimbleDir / "pkgs"
-      nimblePath(path, info)
+      nimblePath(conf, path, info)
   of "nonimblepath", "nobabelpath":
-    expectNoArg(switch, arg, pass, info)
-    disableNimblePath()
+    expectNoArg(conf, switch, arg, pass, info)
+    disableNimblePath(conf)
   of "excludepath":
-    expectArg(switch, arg, pass, info)
-    let path = processPath(arg, info)
+    expectArg(conf, switch, arg, pass, info)
+    let path = processPath(conf, arg, info)
 
-    options.searchPaths.keepItIf( cmpPaths(it, path) != 0 )
-    options.lazyPaths.keepItIf( cmpPaths(it, path) != 0 )
+    conf.searchPaths.keepItIf(cmpPaths(it, path) != 0)
+    conf.lazyPaths.keepItIf(cmpPaths(it, path) != 0)
 
     if (len(path) > 0) and (path[len(path) - 1] == DirSep):
       let strippedPath = path[0 .. (len(path) - 2)]
-      options.searchPaths.keepItIf( cmpPaths(it, strippedPath) != 0 )
-      options.lazyPaths.keepItIf( cmpPaths(it, strippedPath) != 0 )
+      conf.searchPaths.keepItIf(cmpPaths(it, strippedPath) != 0)
+      conf.lazyPaths.keepItIf(cmpPaths(it, strippedPath) != 0)
   of "nimcache":
-    expectArg(switch, arg, pass, info)
-    options.nimcacheDir = processPath(arg, info, true)
+    expectArg(conf, switch, arg, pass, info)
+    conf.nimcacheDir = processPath(conf, arg, info, true)
   of "out", "o":
-    expectArg(switch, arg, pass, info)
-    options.outFile = arg
+    expectArg(conf, switch, arg, pass, info)
+    conf.outFile = arg
   of "docseesrcurl":
-    expectArg(switch, arg, pass, info)
-    options.docSeeSrcUrl = arg
+    expectArg(conf, switch, arg, pass, info)
+    conf.docSeeSrcUrl = arg
   of "mainmodule", "m":
     discard "allow for backwards compatibility, but don't do anything"
   of "define", "d":
-    expectArg(switch, arg, pass, info)
+    expectArg(conf, switch, arg, pass, info)
     if {':', '='} in arg:
-      splitSwitch(arg, key, val, pass, info)
-      defineSymbol(key, val)
+      splitSwitch(conf, arg, key, val, pass, info)
+      defineSymbol(conf.symbols, key, val)
     else:
-      defineSymbol(arg)
+      defineSymbol(conf.symbols, arg)
   of "undef", "u":
-    expectArg(switch, arg, pass, info)
-    undefSymbol(arg)
+    expectArg(conf, switch, arg, pass, info)
+    undefSymbol(conf.symbols, arg)
   of "symbol":
-    expectArg(switch, arg, pass, info)
+    expectArg(conf, switch, arg, pass, info)
     # deprecated, do nothing
   of "compile":
-    expectArg(switch, arg, pass, info)
-    if pass in {passCmd2, passPP}: processCompile(arg)
+    expectArg(conf, switch, arg, pass, info)
+    if pass in {passCmd2, passPP}: processCompile(conf, arg)
   of "link":
-    expectArg(switch, arg, pass, info)
-    if pass in {passCmd2, passPP}: addExternalFileToLink(arg)
+    expectArg(conf, switch, arg, pass, info)
+    if pass in {passCmd2, passPP}: addExternalFileToLink(conf, arg)
   of "debuginfo":
-    expectNoArg(switch, arg, pass, info)
-    incl(gGlobalOptions, optCDebug)
+    expectNoArg(conf, switch, arg, pass, info)
+    incl(conf.globalOptions, optCDebug)
   of "embedsrc":
-    expectNoArg(switch, arg, pass, info)
-    incl(gGlobalOptions, optEmbedOrigSrc)
+    expectNoArg(conf, switch, arg, pass, info)
+    incl(conf.globalOptions, optEmbedOrigSrc)
   of "compileonly", "c":
-    expectNoArg(switch, arg, pass, info)
-    incl(gGlobalOptions, optCompileOnly)
+    expectNoArg(conf, switch, arg, pass, info)
+    incl(conf.globalOptions, optCompileOnly)
   of "nolinking":
-    expectNoArg(switch, arg, pass, info)
-    incl(gGlobalOptions, optNoLinking)
+    expectNoArg(conf, switch, arg, pass, info)
+    incl(conf.globalOptions, optNoLinking)
   of "nomain":
-    expectNoArg(switch, arg, pass, info)
-    incl(gGlobalOptions, optNoMain)
+    expectNoArg(conf, switch, arg, pass, info)
+    incl(conf.globalOptions, optNoMain)
   of "forcebuild", "f":
-    expectNoArg(switch, arg, pass, info)
-    incl(gGlobalOptions, optForceFullMake)
+    expectNoArg(conf, switch, arg, pass, info)
+    incl(conf.globalOptions, optForceFullMake)
   of "project":
-    expectNoArg(switch, arg, pass, info)
-    gWholeProject = true
+    expectNoArg(conf, switch, arg, pass, info)
+    incl conf.globalOptions, optWholeProject
   of "gc":
-    expectArg(switch, arg, pass, info)
+    expectArg(conf, switch, arg, pass, info)
     case arg.normalize
     of "boehm":
-      gSelectedGC = gcBoehm
-      defineSymbol("boehmgc")
+      conf.selectedGC = gcBoehm
+      defineSymbol(conf.symbols, "boehmgc")
     of "refc":
-      gSelectedGC = gcRefc
+      conf.selectedGC = gcRefc
     of "v2":
-      gSelectedGC = gcV2
+      conf.selectedGC = gcV2
     of "markandsweep":
-      gSelectedGC = gcMarkAndSweep
-      defineSymbol("gcmarkandsweep")
+      conf.selectedGC = gcMarkAndSweep
+      defineSymbol(conf.symbols, "gcmarkandsweep")
     of "generational":
-      gSelectedGC = gcGenerational
-      defineSymbol("gcgenerational")
+      conf.selectedGC = gcGenerational
+      defineSymbol(conf.symbols, "gcgenerational")
     of "go":
-      gSelectedGC = gcGo
-      defineSymbol("gogc")
+      conf.selectedGC = gcGo
+      defineSymbol(conf.symbols, "gogc")
     of "none":
-      gSelectedGC = gcNone
-      defineSymbol("nogc")
+      conf.selectedGC = gcNone
+      defineSymbol(conf.symbols, "nogc")
     of "stack", "regions":
-      gSelectedGC= gcRegions
-      defineSymbol("gcregions")
-    else: localError(info, errNoneBoehmRefcExpectedButXFound, arg)
+      conf.selectedGC= gcRegions
+      defineSymbol(conf.symbols, "gcregions")
+    else: localError(conf, info, errNoneBoehmRefcExpectedButXFound % arg)
   of "warnings", "w":
-    if processOnOffSwitchOrList({optWarns}, arg, pass, info): listWarnings()
-  of "warning": processSpecificNote(arg, wWarning, pass, info, switch)
-  of "hint": processSpecificNote(arg, wHint, pass, info, switch)
+    if processOnOffSwitchOrList(conf, {optWarns}, arg, pass, info): listWarnings(conf)
+  of "warning": processSpecificNote(arg, wWarning, pass, info, switch, conf)
+  of "hint": processSpecificNote(arg, wHint, pass, info, switch, conf)
   of "hints":
-    if processOnOffSwitchOrList({optHints}, arg, pass, info): listHints()
-  of "threadanalysis": processOnOffSwitchG({optThreadAnalysis}, arg, pass, info)
-  of "stacktrace": processOnOffSwitch({optStackTrace}, arg, pass, info)
-  of "excessivestacktrace": processOnOffSwitchG({optExcessiveStackTrace}, arg, pass, info)
-  of "linetrace": processOnOffSwitch({optLineTrace}, arg, pass, info)
+    if processOnOffSwitchOrList(conf, {optHints}, arg, pass, info): listHints(conf)
+  of "threadanalysis": processOnOffSwitchG(conf, {optThreadAnalysis}, arg, pass, info)
+  of "stacktrace": processOnOffSwitch(conf, {optStackTrace}, arg, pass, info)
+  of "excessivestacktrace": processOnOffSwitchG(conf, {optExcessiveStackTrace}, arg, pass, info)
+  of "linetrace": processOnOffSwitch(conf, {optLineTrace}, arg, pass, info)
   of "debugger":
     case arg.normalize
     of "on", "endb":
-      gOptions.incl optEndb
-      defineSymbol("endb")
+      conf.options.incl optEndb
+      defineSymbol(conf.symbols, "endb")
     of "off":
-      gOptions.excl optEndb
-      undefSymbol("endb")
+      conf.options.excl optEndb
+      undefSymbol(conf.symbols, "endb")
     of "native", "gdb":
-      incl(gGlobalOptions, optCDebug)
-      gOptions = gOptions + {optLineDir} - {optEndb}
-      defineSymbol("nimTypeNames", nil) # type names are used in gdb pretty printing
-      undefSymbol("endb")
+      incl(conf.globalOptions, optCDebug)
+      conf.options = conf.options + {optLineDir} - {optEndb}
+      defineSymbol(conf.symbols, "nimTypeNames") # type names are used in gdb pretty printing
+      undefSymbol(conf.symbols, "endb")
     else:
-      localError(info, "expected endb|gdb but found " & arg)
+      localError(conf, info, "expected endb|gdb but found " & arg)
   of "profiler":
-    processOnOffSwitch({optProfiler}, arg, pass, info)
-    if optProfiler in gOptions: defineSymbol("profiler")
-    else: undefSymbol("profiler")
+    processOnOffSwitch(conf, {optProfiler}, arg, pass, info)
+    if optProfiler in conf.options: defineSymbol(conf.symbols, "profiler")
+    else: undefSymbol(conf.symbols, "profiler")
   of "memtracker":
-    processOnOffSwitch({optMemTracker}, arg, pass, info)
-    if optMemTracker in gOptions: defineSymbol("memtracker")
-    else: undefSymbol("memtracker")
+    processOnOffSwitch(conf, {optMemTracker}, arg, pass, info)
+    if optMemTracker in conf.options: defineSymbol(conf.symbols, "memtracker")
+    else: undefSymbol(conf.symbols, "memtracker")
   of "hotcodereloading":
-    processOnOffSwitch({optHotCodeReloading}, arg, pass, info)
-    if optHotCodeReloading in gOptions: defineSymbol("hotcodereloading")
-    else: undefSymbol("hotcodereloading")
+    processOnOffSwitch(conf, {optHotCodeReloading}, arg, pass, info)
+    if optHotCodeReloading in conf.options: defineSymbol(conf.symbols, "hotcodereloading")
+    else: undefSymbol(conf.symbols, "hotcodereloading")
   of "oldnewlines":
     case arg.normalize
     of "on":
-      options.gOldNewlines = true
-      defineSymbol("nimOldNewlines")
+      conf.oldNewlines = true
+      defineSymbol(conf.symbols, "nimOldNewlines")
     of "off":
-      options.gOldNewlines = false
-      undefSymbol("nimOldNewlines")
+      conf.oldNewlines = false
+      undefSymbol(conf.symbols, "nimOldNewlines")
     else:
-      localError(info, errOnOrOffExpectedButXFound, arg)
-  of "checks", "x": processOnOffSwitch(ChecksOptions, arg, pass, info)
+      localError(conf, info, errOnOrOffExpectedButXFound % arg)
+  of "laxstrings": processOnOffSwitch(conf, {optLaxStrings}, arg, pass, info)
+  of "checks", "x": processOnOffSwitch(conf, ChecksOptions, arg, pass, info)
   of "floatchecks":
-    processOnOffSwitch({optNaNCheck, optInfCheck}, arg, pass, info)
-  of "infchecks": processOnOffSwitch({optInfCheck}, arg, pass, info)
-  of "nanchecks": processOnOffSwitch({optNaNCheck}, arg, pass, info)
-  of "nilchecks": processOnOffSwitch({optNilCheck}, arg, pass, info)
-  of "objchecks": processOnOffSwitch({optObjCheck}, arg, pass, info)
-  of "fieldchecks": processOnOffSwitch({optFieldCheck}, arg, pass, info)
-  of "rangechecks": processOnOffSwitch({optRangeCheck}, arg, pass, info)
-  of "boundchecks": processOnOffSwitch({optBoundsCheck}, arg, pass, info)
-  of "overflowchecks": processOnOffSwitch({optOverflowCheck}, arg, pass, info)
-  of "movechecks": processOnOffSwitch({optMoveCheck}, arg, pass, info)
-  of "linedir": processOnOffSwitch({optLineDir}, arg, pass, info)
-  of "assertions", "a": processOnOffSwitch({optAssert}, arg, pass, info)
-  of "deadcodeelim": processOnOffSwitchG({optDeadCodeElim}, arg, pass, info)
+    processOnOffSwitch(conf, {optNaNCheck, optInfCheck}, arg, pass, info)
+  of "infchecks": processOnOffSwitch(conf, {optInfCheck}, arg, pass, info)
+  of "nanchecks": processOnOffSwitch(conf, {optNaNCheck}, arg, pass, info)
+  of "nilchecks": processOnOffSwitch(conf, {optNilCheck}, arg, pass, info)
+  of "objchecks": processOnOffSwitch(conf, {optObjCheck}, arg, pass, info)
+  of "fieldchecks": processOnOffSwitch(conf, {optFieldCheck}, arg, pass, info)
+  of "rangechecks": processOnOffSwitch(conf, {optRangeCheck}, arg, pass, info)
+  of "boundchecks": processOnOffSwitch(conf, {optBoundsCheck}, arg, pass, info)
+  of "overflowchecks": processOnOffSwitch(conf, {optOverflowCheck}, arg, pass, info)
+  of "movechecks": processOnOffSwitch(conf, {optMoveCheck}, arg, pass, info)
+  of "linedir": processOnOffSwitch(conf, {optLineDir}, arg, pass, info)
+  of "assertions", "a": processOnOffSwitch(conf, {optAssert}, arg, pass, info)
+  of "deadcodeelim": discard # deprecated, dead code elim always on
   of "threads":
-    processOnOffSwitchG({optThreads}, arg, pass, info)
-    #if optThreads in gGlobalOptions: incl(gNotes, warnGcUnsafe)
-  of "tlsemulation": processOnOffSwitchG({optTlsEmulation}, arg, pass, info)
-  of "taintmode": processOnOffSwitchG({optTaintMode}, arg, pass, info)
+    processOnOffSwitchG(conf, {optThreads}, arg, pass, info)
+    #if optThreads in conf.globalOptions: incl(conf.notes, warnGcUnsafe)
+  of "tlsemulation": processOnOffSwitchG(conf, {optTlsEmulation}, arg, pass, info)
+  of "taintmode": processOnOffSwitchG(conf, {optTaintMode}, arg, pass, info)
   of "implicitstatic":
-    processOnOffSwitch({optImplicitStatic}, arg, pass, info)
+    processOnOffSwitch(conf, {optImplicitStatic}, arg, pass, info)
   of "patterns":
-    processOnOffSwitch({optPatterns}, arg, pass, info)
+    processOnOffSwitch(conf, {optPatterns}, arg, pass, info)
   of "opt":
-    expectArg(switch, arg, pass, info)
+    expectArg(conf, switch, arg, pass, info)
     case arg.normalize
     of "speed":
-      incl(gOptions, optOptimizeSpeed)
-      excl(gOptions, optOptimizeSize)
+      incl(conf.options, optOptimizeSpeed)
+      excl(conf.options, optOptimizeSize)
     of "size":
-      excl(gOptions, optOptimizeSpeed)
-      incl(gOptions, optOptimizeSize)
+      excl(conf.options, optOptimizeSpeed)
+      incl(conf.options, optOptimizeSize)
     of "none":
-      excl(gOptions, optOptimizeSpeed)
-      excl(gOptions, optOptimizeSize)
-    else: localError(info, errNoneSpeedOrSizeExpectedButXFound, arg)
+      excl(conf.options, optOptimizeSpeed)
+      excl(conf.options, optOptimizeSize)
+    else: localError(conf, info, errNoneSpeedOrSizeExpectedButXFound % arg)
   of "app":
-    expectArg(switch, arg, pass, info)
+    expectArg(conf, switch, arg, pass, info)
     case arg.normalize
     of "gui":
-      incl(gGlobalOptions, optGenGuiApp)
-      defineSymbol("executable")
-      defineSymbol("guiapp")
+      incl(conf.globalOptions, optGenGuiApp)
+      defineSymbol(conf.symbols, "executable")
+      defineSymbol(conf.symbols, "guiapp")
     of "console":
-      excl(gGlobalOptions, optGenGuiApp)
-      defineSymbol("executable")
-      defineSymbol("consoleapp")
+      excl(conf.globalOptions, optGenGuiApp)
+      defineSymbol(conf.symbols, "executable")
+      defineSymbol(conf.symbols, "consoleapp")
     of "lib":
-      incl(gGlobalOptions, optGenDynLib)
-      excl(gGlobalOptions, optGenGuiApp)
-      defineSymbol("library")
-      defineSymbol("dll")
+      incl(conf.globalOptions, optGenDynLib)
+      excl(conf.globalOptions, optGenGuiApp)
+      defineSymbol(conf.symbols, "library")
+      defineSymbol(conf.symbols, "dll")
     of "staticlib":
-      incl(gGlobalOptions, optGenStaticLib)
-      excl(gGlobalOptions, optGenGuiApp)
-      defineSymbol("library")
-      defineSymbol("staticlib")
-    else: localError(info, errGuiConsoleOrLibExpectedButXFound, arg)
+      incl(conf.globalOptions, optGenStaticLib)
+      excl(conf.globalOptions, optGenGuiApp)
+      defineSymbol(conf.symbols, "library")
+      defineSymbol(conf.symbols, "staticlib")
+    else: localError(conf, info, errGuiConsoleOrLibExpectedButXFound % arg)
   of "passc", "t":
-    expectArg(switch, arg, pass, info)
-    if pass in {passCmd2, passPP}: extccomp.addCompileOptionCmd(arg)
+    expectArg(conf, switch, arg, pass, info)
+    if pass in {passCmd2, passPP}: extccomp.addCompileOptionCmd(conf, arg)
   of "passl", "l":
-    expectArg(switch, arg, pass, info)
-    if pass in {passCmd2, passPP}: extccomp.addLinkOptionCmd(arg)
+    expectArg(conf, switch, arg, pass, info)
+    if pass in {passCmd2, passPP}: extccomp.addLinkOptionCmd(conf, arg)
   of "cincludes":
-    expectArg(switch, arg, pass, info)
-    if pass in {passCmd2, passPP}: cIncludes.add arg.processPath(info)
+    expectArg(conf, switch, arg, pass, info)
+    if pass in {passCmd2, passPP}: cIncludes.add processPath(conf, arg, info)
   of "clibdir":
-    expectArg(switch, arg, pass, info)
-    if pass in {passCmd2, passPP}: cLibs.add arg.processPath(info)
+    expectArg(conf, switch, arg, pass, info)
+    if pass in {passCmd2, passPP}: cLibs.add processPath(conf, arg, info)
   of "clib":
-    expectArg(switch, arg, pass, info)
-    if pass in {passCmd2, passPP}: cLinkedLibs.add arg.processPath(info)
+    expectArg(conf, switch, arg, pass, info)
+    if pass in {passCmd2, passPP}: cLinkedLibs.add processPath(conf, arg, info)
   of "header":
-    if config != nil: config.headerFile = arg
-    incl(gGlobalOptions, optGenIndex)
+    if conf != nil: conf.headerFile = arg
+    incl(conf.globalOptions, optGenIndex)
   of "index":
-    processOnOffSwitchG({optGenIndex}, arg, pass, info)
+    processOnOffSwitchG(conf, {optGenIndex}, arg, pass, info)
   of "import":
-    expectArg(switch, arg, pass, info)
-    if pass in {passCmd2, passPP}: implicitImports.add arg
+    expectArg(conf, switch, arg, pass, info)
+    if pass in {passCmd2, passPP}: conf.implicitImports.add arg
   of "include":
-    expectArg(switch, arg, pass, info)
-    if pass in {passCmd2, passPP}: implicitIncludes.add arg
+    expectArg(conf, switch, arg, pass, info)
+    if pass in {passCmd2, passPP}: conf.implicitIncludes.add arg
   of "listcmd":
-    expectNoArg(switch, arg, pass, info)
-    incl(gGlobalOptions, optListCmd)
+    expectNoArg(conf, switch, arg, pass, info)
+    incl(conf.globalOptions, optListCmd)
   of "genmapping":
-    expectNoArg(switch, arg, pass, info)
-    incl(gGlobalOptions, optGenMapping)
+    expectNoArg(conf, switch, arg, pass, info)
+    incl(conf.globalOptions, optGenMapping)
   of "os":
-    expectArg(switch, arg, pass, info)
+    expectArg(conf, switch, arg, pass, info)
     if pass in {passCmd1, passPP}:
       theOS = platform.nameToOS(arg)
-      if theOS == osNone: localError(info, errUnknownOS, arg)
+      if theOS == osNone: localError(conf, info, "unknown OS: '$1'" % arg)
       elif theOS != platform.hostOS:
         setTarget(theOS, targetCPU)
   of "cpu":
-    expectArg(switch, arg, pass, info)
+    expectArg(conf, switch, arg, pass, info)
     if pass in {passCmd1, passPP}:
       cpu = platform.nameToCPU(arg)
-      if cpu == cpuNone: localError(info, errUnknownCPU, arg)
+      if cpu == cpuNone: localError(conf, info, "unknown CPU: '$1'" % arg)
       elif cpu != platform.hostCPU:
         setTarget(targetOS, cpu)
   of "run", "r":
-    expectNoArg(switch, arg, pass, info)
-    incl(gGlobalOptions, optRun)
+    expectNoArg(conf, switch, arg, pass, info)
+    incl(conf.globalOptions, optRun)
   of "verbosity":
-    expectArg(switch, arg, pass, info)
-    gVerbosity = parseInt(arg)
-    gNotes = NotesVerbosity[gVerbosity]
-    incl(gNotes, enableNotes)
-    excl(gNotes, disableNotes)
-    gMainPackageNotes = gNotes
+    expectArg(conf, switch, arg, pass, info)
+    conf.verbosity = parseInt(arg)
+    conf.notes = NotesVerbosity[conf.verbosity]
+    incl(conf.notes, conf.enableNotes)
+    excl(conf.notes, conf.disableNotes)
+    conf.mainPackageNotes = conf.notes
   of "parallelbuild":
-    expectArg(switch, arg, pass, info)
-    gNumberOfProcessors = parseInt(arg)
+    expectArg(conf, switch, arg, pass, info)
+    conf.numberOfProcessors = parseInt(arg)
   of "version", "v":
-    expectNoArg(switch, arg, pass, info)
-    writeVersionInfo(pass)
+    expectNoArg(conf, switch, arg, pass, info)
+    writeVersionInfo(conf, pass)
   of "advanced":
-    expectNoArg(switch, arg, pass, info)
-    writeAdvancedUsage(pass)
+    expectNoArg(conf, switch, arg, pass, info)
+    writeAdvancedUsage(conf, pass)
+  of "fullhelp":
+    expectNoArg(conf, switch, arg, pass, info)
+    writeFullhelp(conf, pass)
   of "help", "h":
-    expectNoArg(switch, arg, pass, info)
-    helpOnError(pass)
+    expectNoArg(conf, switch, arg, pass, info)
+    helpOnError(conf, pass)
   of "symbolfiles":
     case arg.normalize
-    of "on": gSymbolFiles = enabledSf
-    of "off": gSymbolFiles = disabledSf
-    of "writeonly": gSymbolFiles = writeOnlySf
-    of "readonly": gSymbolFiles = readOnlySf
-    of "v2": gSymbolFiles = v2Sf
-    else: localError(info, errOnOrOffExpectedButXFound, arg)
+    of "on": conf.symbolFiles = enabledSf
+    of "off": conf.symbolFiles = disabledSf
+    of "writeonly": conf.symbolFiles = writeOnlySf
+    of "readonly": conf.symbolFiles = readOnlySf
+    of "v2": conf.symbolFiles = v2Sf
+    else: localError(conf, info, "invalid option for --symbolFiles: " & arg)
   of "skipcfg":
-    expectNoArg(switch, arg, pass, info)
-    incl(gGlobalOptions, optSkipConfigFile)
+    expectNoArg(conf, switch, arg, pass, info)
+    incl(conf.globalOptions, optSkipConfigFile)
   of "skipprojcfg":
-    expectNoArg(switch, arg, pass, info)
-    incl(gGlobalOptions, optSkipProjConfigFile)
+    expectNoArg(conf, switch, arg, pass, info)
+    incl(conf.globalOptions, optSkipProjConfigFile)
   of "skipusercfg":
-    expectNoArg(switch, arg, pass, info)
-    incl(gGlobalOptions, optSkipUserConfigFile)
+    expectNoArg(conf, switch, arg, pass, info)
+    incl(conf.globalOptions, optSkipUserConfigFile)
   of "skipparentcfg":
-    expectNoArg(switch, arg, pass, info)
-    incl(gGlobalOptions, optSkipParentConfigFiles)
+    expectNoArg(conf, switch, arg, pass, info)
+    incl(conf.globalOptions, optSkipParentConfigFiles)
   of "genscript", "gendeps":
-    expectNoArg(switch, arg, pass, info)
-    incl(gGlobalOptions, optGenScript)
-  of "colors": processOnOffSwitchG({optUseColors}, arg, pass, info)
+    expectNoArg(conf, switch, arg, pass, info)
+    incl(conf.globalOptions, optGenScript)
+    incl(conf.globalOptions, optCompileOnly)
+  of "colors": processOnOffSwitchG(conf, {optUseColors}, arg, pass, info)
   of "lib":
-    expectArg(switch, arg, pass, info)
-    libpath = processPath(arg, info, notRelativeToProj=true)
+    expectArg(conf, switch, arg, pass, info)
+    conf.libpath = processPath(conf, arg, info, notRelativeToProj=true)
   of "putenv":
-    expectArg(switch, arg, pass, info)
-    splitSwitch(arg, key, val, pass, info)
+    expectArg(conf, switch, arg, pass, info)
+    splitSwitch(conf, arg, key, val, pass, info)
     os.putEnv(key, val)
   of "cc":
-    expectArg(switch, arg, pass, info)
-    setCC(arg)
+    expectArg(conf, switch, arg, pass, info)
+    setCC(conf, arg, info)
   of "track":
-    expectArg(switch, arg, pass, info)
-    track(arg, info)
+    expectArg(conf, switch, arg, pass, info)
+    track(conf, arg, info)
   of "trackdirty":
-    expectArg(switch, arg, pass, info)
-    trackDirty(arg, info)
+    expectArg(conf, switch, arg, pass, info)
+    trackDirty(conf, arg, info)
   of "suggest":
-    expectNoArg(switch, arg, pass, info)
-    gIdeCmd = ideSug
+    expectNoArg(conf, switch, arg, pass, info)
+    conf.ideCmd = ideSug
   of "def":
-    expectNoArg(switch, arg, pass, info)
-    gIdeCmd = ideDef
+    expectNoArg(conf, switch, arg, pass, info)
+    conf.ideCmd = ideDef
   of "eval":
-    expectArg(switch, arg, pass, info)
-    gEvalExpr = arg
+    expectArg(conf, switch, arg, pass, info)
+    conf.evalExpr = arg
   of "context":
-    expectNoArg(switch, arg, pass, info)
-    gIdeCmd = ideCon
+    expectNoArg(conf, switch, arg, pass, info)
+    conf.ideCmd = ideCon
   of "usages":
-    expectNoArg(switch, arg, pass, info)
-    gIdeCmd = ideUse
+    expectNoArg(conf, switch, arg, pass, info)
+    conf.ideCmd = ideUse
   of "stdout":
-    expectNoArg(switch, arg, pass, info)
-    incl(gGlobalOptions, optStdout)
+    expectNoArg(conf, switch, arg, pass, info)
+    incl(conf.globalOptions, optStdout)
   of "listfullpaths":
-    expectNoArg(switch, arg, pass, info)
-    gListFullPaths = true
+    expectNoArg(conf, switch, arg, pass, info)
+    incl conf.globalOptions, optListFullPaths
   of "dynliboverride":
-    dynlibOverride(switch, arg, pass, info)
+    dynlibOverride(conf, switch, arg, pass, info)
   of "dynliboverrideall":
-    expectNoArg(switch, arg, pass, info)
-    gDynlibOverrideAll = true
+    expectNoArg(conf, switch, arg, pass, info)
+    incl conf.globalOptions, optDynlibOverrideAll
   of "cs":
     # only supported for compatibility. Does nothing.
-    expectArg(switch, arg, pass, info)
+    expectArg(conf, switch, arg, pass, info)
   of "experimental":
-    expectNoArg(switch, arg, pass, info)
-    gExperimentalMode = true
+    if arg.len == 0:
+      conf.features.incl oldExperimentalFeatures
+    else:
+      try:
+        conf.features.incl parseEnum[Feature](arg)
+      except ValueError:
+        localError(conf, info, "unknown experimental feature")
   of "nocppexceptions":
-    expectNoArg(switch, arg, pass, info)
-    incl(gGlobalOptions, optNoCppExceptions)
-    defineSymbol("noCppExceptions")
+    expectNoArg(conf, switch, arg, pass, info)
+    incl(conf.globalOptions, optNoCppExceptions)
+    defineSymbol(conf.symbols, "noCppExceptions")
   of "cppdefine":
-    expectArg(switch, arg, pass, info)
-    if config != nil:
-      config.cppDefine(arg)
+    expectArg(conf, switch, arg, pass, info)
+    if conf != nil:
+      conf.cppDefine(arg)
   of "newruntime":
-    expectNoArg(switch, arg, pass, info)
-    newDestructors = true
-    defineSymbol("nimNewRuntime")
+    expectNoArg(conf, switch, arg, pass, info)
+    doAssert(conf != nil)
+    incl(conf.features, destructor)
+    defineSymbol(conf.symbols, "nimNewRuntime")
   of "cppcompiletonamespace":
-    expectNoArg(switch, arg, pass, info)
-    useNimNamespace = true
-    defineSymbol("cppCompileToNamespace")
-    
+    expectNoArg(conf, switch, arg, pass, info)
+    incl conf.globalOptions, optUseNimNamespace
+    defineSymbol(conf.symbols, "cppCompileToNamespace")
   else:
-    if strutils.find(switch, '.') >= 0: options.setConfigVar(switch, arg)
-    else: invalidCmdLineOption(pass, switch, info)
+    if strutils.find(switch, '.') >= 0: options.setConfigVar(conf, switch, arg)
+    else: invalidCmdLineOption(conf, pass, switch, info)
 
-proc processCommand(switch: string, pass: TCmdLinePass) =
-  var cmd, arg: string
-  splitSwitch(switch, cmd, arg, pass, gCmdLineInfo)
-  processSwitch(cmd, arg, pass, gCmdLineInfo)
+template gCmdLineInfo*(): untyped = newLineInfo(config, "command line", 1, 1)
 
+proc processCommand*(switch: string, pass: TCmdLinePass; config: ConfigRef) =
+  var cmd, arg: string
+  splitSwitch(config, switch, cmd, arg, pass, gCmdLineInfo)
+  processSwitch(cmd, arg, pass, gCmdLineInfo, config)
 
-var
-  arguments* = ""
-    # the arguments to be passed to the program that
-    # should be run
 
-proc processSwitch*(pass: TCmdLinePass; p: OptParser) =
+proc processSwitch*(pass: TCmdLinePass; p: OptParser; config: ConfigRef) =
   # hint[X]:off is parsed as (p.key = "hint[X]", p.val = "off")
   # we fix this here
   var bracketLe = strutils.find(p.key, '[')
   if bracketLe >= 0:
     var key = substr(p.key, 0, bracketLe - 1)
     var val = substr(p.key, bracketLe + 1) & ':' & p.val
-    processSwitch(key, val, pass, gCmdLineInfo)
+    processSwitch(key, val, pass, gCmdLineInfo, config)
   else:
-    processSwitch(p.key, p.val, pass, gCmdLineInfo)
+    processSwitch(p.key, p.val, pass, gCmdLineInfo, config)
 
 proc processArgument*(pass: TCmdLinePass; p: OptParser;
-                      argsCount: var int): bool =
+                      argsCount: var int; config: ConfigRef): bool =
   if argsCount == 0:
     # nim filename.nims  is the same as "nim e filename.nims":
     if p.key.endswith(".nims"):
-      options.command = "e"
-      options.gProjectName = unixToNativePath(p.key)
-      arguments = cmdLineRest(p)
+      config.command = "e"
+      config.projectName = unixToNativePath(p.key)
+      config.arguments = cmdLineRest(p)
       result = true
     elif pass != passCmd2:
-      options.command = p.key
+      config.command = p.key
   else:
-    if pass == passCmd1: options.commandArgs.add p.key
+    if pass == passCmd1: config.commandArgs.add p.key
     if argsCount == 1:
       # support UNIX style filenames everywhere for portable build scripts:
-      options.gProjectName = unixToNativePath(p.key)
-      arguments = cmdLineRest(p)
+      config.projectName = unixToNativePath(p.key)
+      config.arguments = cmdLineRest(p)
       result = true
   inc argsCount
diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim
index 028aedb5b..773c0faf9 100644
--- a/compiler/condsyms.nim
+++ b/compiler/condsyms.nim
@@ -12,78 +12,30 @@
 import
   strtabs, platform, strutils, idents
 
-# We need to use a StringTableRef here as defined symbols are always guaranteed
-# to be style insensitive. Otherwise hell would break lose.
-var gSymbols: StringTableRef
-
 const
   catNone = "false"
 
-proc defineSymbol*(symbol: string, value: string = "true") =
-  gSymbols[symbol] = value
-
-proc undefSymbol*(symbol: string) =
-  gSymbols[symbol] = catNone
-
-proc isDefined*(symbol: string): bool =
-  if gSymbols.hasKey(symbol):
-    result = gSymbols[symbol] != catNone
-  elif cmpIgnoreStyle(symbol, CPU[targetCPU].name) == 0:
-    result = true
-  elif cmpIgnoreStyle(symbol, platform.OS[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 "posix", "unix":
-      result = 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}
-    of "bsd":
-      result = 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
-    of "nimrawsetjmp":
-      result = targetOS in {osSolaris, osNetbsd, osFreebsd, osOpenbsd,
-                            osDragonfly, osMacosx}
-    else: discard
-
-proc isDefined*(symbol: PIdent): bool = isDefined(symbol.s)
+proc defineSymbol*(symbols: StringTableRef; symbol: string, value: string = "true") =
+  symbols[symbol] = value
 
-proc lookupSymbol*(symbol: string): string =
-  result = if isDefined(symbol): gSymbols[symbol] else: nil
+proc undefSymbol*(symbols: StringTableRef; symbol: string) =
+  symbols[symbol] = catNone
 
-proc lookupSymbol*(symbol: PIdent): string = lookupSymbol(symbol.s)
+#proc lookupSymbol*(symbols: StringTableRef; symbol: string): string =
+#  result = if isDefined(symbol): gSymbols[symbol] else: nil
 
-iterator definedSymbolNames*: string =
-  for key, val in pairs(gSymbols):
+iterator definedSymbolNames*(symbols: StringTableRef): string =
+  for key, val in pairs(symbols):
     if val != catNone: yield key
 
-proc countDefinedSymbols*(): int =
+proc countDefinedSymbols*(symbols: StringTableRef): int =
   result = 0
-  for key, val in pairs(gSymbols):
+  for key, val in pairs(symbols):
     if val != catNone: inc(result)
 
-proc initDefines*() =
-  gSymbols = newStringTable(modeStyleInsensitive)
-  defineSymbol("nimrod") # 'nimrod' is always defined
+proc initDefines*(symbols: StringTableRef) =
   # for bootstrapping purposes and old code:
+  template defineSymbol(s) = symbols.defineSymbol(s)
   defineSymbol("nimhygiene")
   defineSymbol("niminheritable")
   defineSymbol("nimmixin")
@@ -115,3 +67,6 @@ proc initDefines*() =
   defineSymbol("nimHasNilChecks")
   defineSymbol("nimSymKind")
   defineSymbol("nimVmEqIdent")
+  defineSymbol("nimNoNil")
+  defineSymbol("nimNoZeroTerminator")
+  defineSymbol("nimNotNil")
diff --git a/compiler/configuration.nim b/compiler/configuration.nim
new file mode 100644
index 000000000..f9f0e623c
--- /dev/null
+++ b/compiler/configuration.nim
@@ -0,0 +1,360 @@
+#
+#
+#           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 rather excessive configuration object that
+## needs to be passed around to everything so that the compiler becomes
+## more useful as a library.
+
+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",
+,
+]#
diff --git a/compiler/depends.nim b/compiler/depends.nim
index 2b600c1da..732404232 100644
--- a/compiler/depends.nim
+++ b/compiler/depends.nim
@@ -19,6 +19,7 @@ proc generateDot*(project: string)
 type
   TGen = object of TPassContext
     module*: PSym
+    config: ConfigRef
   PGen = ref TGen
 
 var gDotGraph: Rope # the generated DOT file; we need a global variable
@@ -33,10 +34,10 @@ proc addDotDependency(c: PPassContext, n: PNode): PNode =
   case n.kind
   of nkImportStmt:
     for i in countup(0, sonsLen(n) - 1):
-      var imported = getModuleName(n.sons[i])
+      var imported = getModuleName(g.config, n.sons[i])
       addDependencyAux(g.module.name.s, imported)
   of nkFromStmt, nkImportExceptStmt:
-    var imported = getModuleName(n.sons[0])
+    var imported = getModuleName(g.config, n.sons[0])
     addDependencyAux(g.module.name.s, imported)
   of nkStmtList, nkBlockStmt, nkStmtListExpr, nkBlockExpr:
     for i in countup(0, sonsLen(n) - 1): discard addDotDependency(c, n.sons[i])
@@ -52,6 +53,7 @@ proc myOpen(graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext =
   var g: PGen
   new(g)
   g.module = module
+  g.config = graph.config
   result = g
 
 const gendependPass* = makePass(open = myOpen, process = addDotDependency)
diff --git a/compiler/destroyer.nim b/compiler/destroyer.nim
index ffa3e37e5..31c735794 100644
--- a/compiler/destroyer.nim
+++ b/compiler/destroyer.nim
@@ -116,7 +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
+  strutils, options, dfa, lowerings, rodread, tables, modulegraphs,
+  configuration
 
 const
   InterestingSyms = {skVar, skResult, skLet}
@@ -130,6 +131,7 @@ type
     tmp: PSym
     destroys, topLevelVars: PNode
     toDropBit: Table[int, PSym]
+    graph: ModuleGraph
 
 proc getTemp(c: var Con; typ: PType; info: TLineInfo): PNode =
   # XXX why are temps fields in an object here?
@@ -222,21 +224,21 @@ proc patchHead(s: PSym) =
 template genOp(opr, opname) =
   let op = opr
   if op == nil:
-    globalError(dest.info, "internal error: '" & opname & "' operator not found for type " & typeToString(t))
+    globalError(c.graph.config, dest.info, "internal error: '" & opname & "' operator not found for type " & typeToString(t))
   elif op.ast[genericParamsPos].kind != nkEmpty:
-    globalError(dest.info, "internal error: '" & opname & "' operator is generic")
+    globalError(c.graph.config, dest.info, "internal error: '" & opname & "' operator is generic")
   patchHead op
   result = newTree(nkCall, newSymNode(op), newTree(nkHiddenAddr, dest))
 
-proc genSink(t: PType; dest: PNode): PNode =
+proc genSink(c: Con; t: PType; dest: PNode): PNode =
   let t = t.skipTypes({tyGenericInst, tyAlias, tySink})
   genOp(if t.sink != nil: t.sink else: t.assignment, "=sink")
 
-proc genCopy(t: PType; dest: PNode): PNode =
+proc genCopy(c: Con; t: PType; dest: PNode): PNode =
   let t = t.skipTypes({tyGenericInst, tyAlias, tySink})
   genOp(t.assignment, "=")
 
-proc genDestroy(t: PType; dest: PNode): PNode =
+proc genDestroy(c: Con; t: PType; dest: PNode): PNode =
   let t = t.skipTypes({tyGenericInst, tyAlias, tySink})
   genOp(t.destructor, "=destroy")
 
@@ -249,14 +251,14 @@ proc dropBit(c: var Con; s: PSym): PSym =
 
 proc registerDropBit(c: var Con; s: PSym) =
   let result = newSym(skTemp, getIdent(s.name.s & "_AliveBit"), c.owner, s.info)
-  result.typ = getSysType(tyBool)
+  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.toDropBit[s.id] = result
   # generate:
   #  if not sinkParam_AliveBit: `=destroy`(sinkParam)
   c.destroys.add newTree(nkIfStmt,
-    newTree(nkElifBranch, newSymNode result, genDestroy(s.typ, newSymNode s)))
+    newTree(nkElifBranch, newSymNode result, genDestroy(c, s.typ, newSymNode s)))
 
 proc p(n: PNode; c: var Con): PNode
 
@@ -274,36 +276,36 @@ proc destructiveMoveSink(n: PNode; c: var Con): PNode =
   result = newNodeIT(nkStmtListExpr, n.info, n.typ)
   let bit = newSymNode dropBit(c, n.sym)
   if optMoveCheck in c.owner.options:
-    result.add callCodegenProc("chckMove", bit)
+    result.add callCodegenProc(c.graph, "chckMove", bit)
   result.add newTree(nkAsgn, bit,
-    newIntTypeNode(nkIntLit, 0, getSysType(tyBool)))
+    newIntTypeNode(nkIntLit, 0, getSysType(c.graph, n.info, tyBool)))
   result.add n
 
 proc moveOrCopy(dest, ri: PNode; c: var Con): PNode =
   if ri.kind in constrExprs:
-    result = genSink(ri.typ, dest)
+    result = genSink(c, ri.typ, dest)
     # watch out and no not transform 'ri' twice if it's a call:
     let ri2 = copyNode(ri)
     recurse(ri, ri2)
     result.add ri2
   elif ri.kind == nkSym and isHarmlessVar(ri.sym, c):
-    result = genSink(ri.typ, dest)
+    result = genSink(c, ri.typ, dest)
     result.add p(ri, c)
   elif ri.kind == nkSym and isSinkParam(ri.sym):
-    result = genSink(ri.typ, dest)
+    result = genSink(c, ri.typ, dest)
     result.add destructiveMoveSink(ri, c)
   else:
-    result = genCopy(ri.typ, dest)
+    result = genCopy(c, ri.typ, dest)
     result.add p(ri, c)
 
 proc passCopyToSink(n: PNode; c: var Con): PNode =
   result = newNodeIT(nkStmtListExpr, n.info, n.typ)
   let tmp = getTemp(c, n.typ, n.info)
   if hasDestructor(n.typ):
-    var m = genCopy(n.typ, tmp)
+    var m = genCopy(c, n.typ, tmp)
     m.add p(n, c)
     result.add m
-    message(n.info, hintPerformance,
+    message(c.graph.config, n.info, hintPerformance,
       "passing '$1' to a sink parameter introduces an implicit copy; " &
       "use 'move($1)' to prevent it" % $n)
   else:
@@ -312,7 +314,7 @@ proc passCopyToSink(n: PNode; c: var Con): PNode =
 
 proc genReset(n: PNode; c: var Con): PNode =
   result = newNodeI(nkCall, n.info)
-  result.add(newSymNode(createMagic("reset", mReset)))
+  result.add(newSymNode(createMagic(c.graph, "reset", mReset)))
   # The mReset builtin does not take the address:
   result.add n
 
@@ -345,7 +347,7 @@ proc p(n: PNode; c: var Con): PNode =
       let L = it.len-1
       let ri = it[L]
       if it.kind == nkVarTuple and hasDestructor(ri.typ):
-        let x = lowerTupleUnpacking(it, c.owner)
+        let x = lowerTupleUnpacking(c.graph, it, c.owner)
         result.add p(x, c)
       elif it.kind == nkIdentDefs and hasDestructor(it[0].typ):
         for j in 0..L-2:
@@ -354,7 +356,7 @@ proc p(n: PNode; c: var Con): PNode =
           # move the variable declaration to the top of the frame:
           c.addTopVar v
           # make sure it's destroyed at the end of the proc:
-          c.destroys.add genDestroy(v.typ, v)
+          c.destroys.add genDestroy(c, v.typ, v)
           if ri.kind != nkEmpty:
             let r = moveOrCopy(v, ri, c)
             result.add r
@@ -400,11 +402,11 @@ proc p(n: PNode; c: var Con): PNode =
       discard "produce temp creation"
       result = newNodeIT(nkStmtListExpr, n.info, n.typ)
       let tmp = getTemp(c, n.typ, n.info)
-      var sinkExpr = genSink(n.typ, tmp)
+      var sinkExpr = genSink(c, n.typ, tmp)
       sinkExpr.add n
       result.add sinkExpr
       result.add tmp
-      c.destroys.add genDestroy(n.typ, tmp)
+      c.destroys.add genDestroy(c, n.typ, tmp)
     else:
       result = n
   of nkAsgn, nkFastAsgn:
@@ -420,16 +422,18 @@ proc p(n: PNode; c: var Con): PNode =
     result = copyNode(n)
     recurse(n, result)
 
-proc injectDestructorCalls*(owner: PSym; n: PNode): PNode =
+proc injectDestructorCalls*(g: ModuleGraph; owner: PSym; n: PNode): PNode =
   when defined(nimDebugDestroys):
     echo "injecting into ", n
   var c: Con
   c.owner = owner
   c.tmp = newSym(skTemp, getIdent":d", owner, n.info)
-  c.tmpObj = createObj(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
   let cfg = constructCfg(owner, n)
   shallowCopy(c.g, cfg)
   c.jumpTargets = initIntSet()
diff --git a/compiler/dfa.nim b/compiler/dfa.nim
index bc9d13870..0fd706178 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
+import ast, astalgo, types, intsets, tables, msgs, options
 
 type
   InstrKind* = enum
@@ -102,14 +102,14 @@ proc genLabel(c: Con): TPosition =
 
 proc jmpBack(c: var Con, n: PNode, p = TPosition(0)) =
   let dist = p.int - c.code.len
-  internalAssert(-0x7fff < dist and dist < 0x7fff)
+  doAssert(-0x7fff < dist and dist < 0x7fff)
   c.code.add Instr(n: n, kind: goto, dest: dist)
 
 proc patch(c: var Con, p: TPosition) =
   # patch with current index
   let p = p.int
   let diff = c.code.len - p
-  internalAssert(-0x7fff < diff and diff < 0x7fff)
+  doAssert(-0x7fff < diff and diff < 0x7fff)
   c.code[p].dest = diff
 
 proc popBlock(c: var Con; oldLen: int) =
@@ -160,7 +160,7 @@ proc genBreak(c: var Con; n: PNode) =
       if c.blocks[i].label == n.sons[0].sym:
         c.blocks[i].fixups.add L1
         return
-    globalError(n.info, errGenerated, "VM problem: cannot find 'break' target")
+    #globalError(n.info, "VM problem: cannot find 'break' target")
   else:
     c.blocks[c.blocks.high].fixups.add L1
 
@@ -334,7 +334,7 @@ proc gen(c: var Con; n: PNode) =
   of nkVarSection, nkLetSection: genVarSection(c, n)
   else: discard
 
-proc dfa(code: seq[Instr]) =
+proc dfa(code: seq[Instr]; conf: ConfigRef) =
   var u = newSeq[IntSet](code.len) # usages
   var d = newSeq[IntSet](code.len) # defs
   var c = newSeq[IntSet](code.len) # consumed
@@ -426,17 +426,17 @@ proc dfa(code: seq[Instr]) =
     of use, useWithinCall:
       let s = code[i].sym
       if s.id notin d[i]:
-        localError(code[i].n.info, "usage of uninitialized variable: " & s.name.s)
+        localError(conf, code[i].n.info, "usage of uninitialized variable: " & s.name.s)
       if s.id in c[i]:
-        localError(code[i].n.info, "usage of an already consumed variable: " & s.name.s)
+        localError(conf, code[i].n.info, "usage of an already consumed variable: " & s.name.s)
 
     else: discard
 
-proc dataflowAnalysis*(s: PSym; body: PNode) =
+proc dataflowAnalysis*(s: PSym; body: PNode; conf: ConfigRef) =
   var c = Con(code: @[], blocks: @[])
   gen(c, body)
   when defined(useDfa) and defined(debugDfa): echoCfg(c.code)
-  dfa(c.code)
+  dfa(c.code, conf)
 
 proc constructCfg*(s: PSym; body: PNode): ControlFlowGraph =
   ## constructs a control flow graph for ``body``.
diff --git a/compiler/docgen.nim b/compiler/docgen.nim
index 74fb305ac..7ab2f0eee 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
+  typesrenderer, astalgo, modulepaths, configuration
 
 type
   TSections = array[TSymKind, Rope]
@@ -29,6 +29,7 @@ type
     jArray: JsonNode
     types: TStrTable
     isPureRst: bool
+    conf*: ConfigRef
 
   PDoc* = ref TDocumentor ## Alias to type less.
 
@@ -53,42 +54,47 @@ proc attachToType(d: PDoc; p: PSym): PSym =
   if params.len > 0: check(0)
   for i in 2..<params.len: check(i)
 
-proc compilerMsgHandler(filename: string, line, col: int,
-                        msgKind: rst.MsgKind, arg: string) {.procvar.} =
-  # translate msg kind:
-  var k: msgs.TMsgKind
-  case msgKind
-  of meCannotOpenFile: k = errCannotOpenFile
-  of meExpected: k = errXExpected
-  of meGridTableNotImplemented: k = errGridTableNotImplemented
-  of meNewSectionExpected: k = errNewSectionExpected
-  of meGeneralParseError: k = errGeneralParseError
-  of meInvalidDirective: k = errInvalidDirectiveX
-  of mwRedefinitionOfLabel: k = warnRedefinitionOfLabel
-  of mwUnknownSubstitution: k = warnUnknownSubstitutionX
-  of mwUnsupportedLanguage: k = warnLanguageXNotSupported
-  of mwUnsupportedField: k = warnFieldXNotSupported
-  globalError(newLineInfo(filename, line, col), k, arg)
-
-proc docgenFindFile(s: string): string {.procvar.} =
-  result = options.findFile(s)
-  if result.len == 0:
-    result = getCurrentDir() / s
-    if not existsFile(result): result = ""
+template declareClosures =
+  proc compilerMsgHandler(filename: string, line, col: int,
+                          msgKind: rst.MsgKind, arg: string) {.procvar.} =
+    # translate msg kind:
+    var k: TMsgKind
+    case msgKind
+    of meCannotOpenFile: k = errCannotOpenFile
+    of meExpected: k = errXExpected
+    of meGridTableNotImplemented: k = errGridTableNotImplemented
+    of meNewSectionExpected: k = errNewSectionExpected
+    of meGeneralParseError: k = errGeneralParseError
+    of meInvalidDirective: k = errInvalidDirectiveX
+    of mwRedefinitionOfLabel: k = warnRedefinitionOfLabel
+    of mwUnknownSubstitution: k = warnUnknownSubstitutionX
+    of mwUnsupportedLanguage: k = warnLanguageXNotSupported
+    of mwUnsupportedField: k = warnFieldXNotSupported
+    globalError(conf, newLineInfo(conf, filename, line, col), k, arg)
+
+  proc docgenFindFile(s: string): string {.procvar.} =
+    result = options.findFile(conf, s)
+    if result.len == 0:
+      result = getCurrentDir() / s
+      if not existsFile(result): result = ""
 
 proc parseRst(text, filename: string,
               line, column: int, hasToc: var bool,
-              rstOptions: RstParseOptions): PRstNode =
+              rstOptions: RstParseOptions;
+              conf: ConfigRef): PRstNode =
+  declareClosures()
   result = rstParse(text, filename, line, column, hasToc, rstOptions,
                     docgenFindFile, compilerMsgHandler)
 
-proc newDocumentor*(filename: string, config: StringTableRef): PDoc =
+proc newDocumentor*(filename: string, conf: ConfigRef): PDoc =
+  declareClosures()
   new(result)
-  initRstGenerator(result[], (if gCmd != cmdRst2tex: outHtml else: outLatex),
-                   options.gConfigVars, filename, {roSupportRawDirective},
+  result.conf = conf
+  initRstGenerator(result[], (if conf.cmd != cmdRst2tex: outHtml else: outLatex),
+                   conf.configVars, filename, {roSupportRawDirective},
                    docgenFindFile, compilerMsgHandler)
 
-  if config.hasKey("doc.googleAnalytics"):
+  if conf.configVars.hasKey("doc.googleAnalytics"):
     result.analytics = """
 <script>
   (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
@@ -100,7 +106,7 @@ proc newDocumentor*(filename: string, config: StringTableRef): PDoc =
   ga('send', 'pageview');
 
 </script>
-    """ % [config.getOrDefault"doc.googleAnalytics"]
+    """ % [conf.configVars.getOrDefault"doc.googleAnalytics"]
   else:
     result.analytics = ""
 
@@ -109,10 +115,10 @@ proc newDocumentor*(filename: string, config: StringTableRef): PDoc =
   result.jArray = newJArray()
   initStrTable result.types
   result.onTestSnippet = proc (d: var RstGenerator; filename, cmd: string; status: int; content: string) =
-    localError(newLineInfo(d.filename, -1, -1), warnUser, "only 'rst2html' supports the ':test:' attribute")
+    localError(conf, newLineInfo(conf, d.filename, -1, -1), warnUser, "only 'rst2html' supports the ':test:' attribute")
 
-proc dispA(dest: var Rope, xml, tex: string, args: openArray[Rope]) =
-  if gCmd != cmdRst2tex: addf(dest, xml, args)
+proc dispA(conf: ConfigRef; dest: var Rope, xml, tex: string, args: openArray[Rope]) =
+  if conf.cmd != cmdRst2tex: addf(dest, xml, args)
   else: addf(dest, tex, args)
 
 proc getVarIdx(varnames: openArray[string], id: string): int =
@@ -121,7 +127,8 @@ proc getVarIdx(varnames: openArray[string], id: string): int =
       return i
   result = -1
 
-proc ropeFormatNamedVars(frmt: FormatStr, varnames: openArray[string],
+proc ropeFormatNamedVars(conf: ConfigRef; frmt: FormatStr,
+                         varnames: openArray[string],
                          varvalues: openArray[Rope]): Rope =
   var i = 0
   var L = len(frmt)
@@ -144,7 +151,8 @@ proc ropeFormatNamedVars(frmt: FormatStr, varnames: openArray[string],
           j = (j * 10) + ord(frmt[i]) - ord('0')
           inc(i)
           if (i > L + 0 - 1) or not (frmt[i] in {'0'..'9'}): break
-        if j > high(varvalues) + 1: internalError("ropeFormatNamedVars")
+        if j > high(varvalues) + 1:
+          rawMessage(conf, errGenerated, "Invalid format string; too many $s: " & frmt)
         num = j
         add(result, varvalues[j - 1])
       of 'A'..'Z', 'a'..'z', '\x80'..'\xFF':
@@ -155,20 +163,23 @@ proc ropeFormatNamedVars(frmt: FormatStr, varnames: openArray[string],
           if not (frmt[i] in {'A'..'Z', '_', 'a'..'z', '\x80'..'\xFF'}): break
         var idx = getVarIdx(varnames, id)
         if idx >= 0: add(result, varvalues[idx])
-        else: rawMessage(errUnknownSubstitionVar, id)
+        else: rawMessage(conf, errGenerated, "unknown substition variable: " & id)
       of '{':
         var id = ""
         inc(i)
-        while frmt[i] != '}':
-          if frmt[i] == '\0': rawMessage(errTokenExpected, "}")
+        while i < frmt.len and frmt[i] != '}':
           add(id, frmt[i])
           inc(i)
-        inc(i)                # skip }
-                              # search for the variable:
-        var idx = getVarIdx(varnames, id)
+        if i >= frmt.len:
+          rawMessage(conf, errGenerated, "expected closing '}'")
+        else:
+          inc(i)                # skip }
+        # search for the variable:
+        let idx = getVarIdx(varnames, id)
         if idx >= 0: add(result, varvalues[idx])
-        else: rawMessage(errUnknownSubstitionVar, id)
-      else: internalError("ropeFormatNamedVars")
+        else: rawMessage(conf, errGenerated, "unknown substition variable: " & id)
+      else:
+        add(result, "$")
     var start = i
     while i < L:
       if frmt[i] != '$': inc(i)
@@ -181,7 +192,7 @@ proc genComment(d: PDoc, n: PNode): string =
   if n.comment != nil:
     renderRstToOut(d[], parseRst(n.comment, toFilename(n.info),
                                toLinenumber(n.info), toColumn(n.info),
-                               dummyHasToc, d.options), result)
+                               dummyHasToc, d.options, d.conf), result)
 
 proc genRecComment(d: PDoc, n: PNode): Rope =
   if n == nil: return nil
@@ -220,37 +231,37 @@ proc nodeToHighlightedHtml(d: PDoc; n: PNode; result: var Rope; renderFlags: TRe
     of tkEof:
       break
     of tkComment:
-      dispA(result, "<span class=\"Comment\">$1</span>", "\\spanComment{$1}",
+      dispA(d.conf, result, "<span class=\"Comment\">$1</span>", "\\spanComment{$1}",
             [rope(esc(d.target, literal))])
     of tokKeywordLow..tokKeywordHigh:
-      dispA(result, "<span class=\"Keyword\">$1</span>", "\\spanKeyword{$1}",
+      dispA(d.conf, result, "<span class=\"Keyword\">$1</span>", "\\spanKeyword{$1}",
             [rope(literal)])
     of tkOpr:
-      dispA(result, "<span class=\"Operator\">$1</span>", "\\spanOperator{$1}",
+      dispA(d.conf, result, "<span class=\"Operator\">$1</span>", "\\spanOperator{$1}",
             [rope(esc(d.target, literal))])
     of tkStrLit..tkTripleStrLit:
-      dispA(result, "<span class=\"StringLit\">$1</span>",
+      dispA(d.conf, result, "<span class=\"StringLit\">$1</span>",
             "\\spanStringLit{$1}", [rope(esc(d.target, literal))])
     of tkCharLit:
-      dispA(result, "<span class=\"CharLit\">$1</span>", "\\spanCharLit{$1}",
+      dispA(d.conf, result, "<span class=\"CharLit\">$1</span>", "\\spanCharLit{$1}",
             [rope(esc(d.target, literal))])
     of tkIntLit..tkUInt64Lit:
-      dispA(result, "<span class=\"DecNumber\">$1</span>",
+      dispA(d.conf, result, "<span class=\"DecNumber\">$1</span>",
             "\\spanDecNumber{$1}", [rope(esc(d.target, literal))])
     of tkFloatLit..tkFloat128Lit:
-      dispA(result, "<span class=\"FloatNumber\">$1</span>",
+      dispA(d.conf, result, "<span class=\"FloatNumber\">$1</span>",
             "\\spanFloatNumber{$1}", [rope(esc(d.target, literal))])
     of tkSymbol:
-      dispA(result, "<span class=\"Identifier\">$1</span>",
+      dispA(d.conf, result, "<span class=\"Identifier\">$1</span>",
             "\\spanIdentifier{$1}", [rope(esc(d.target, literal))])
     of tkSpaces, tkInvalid:
       add(result, literal)
     of tkCurlyDotLe:
-      dispA(result, """<span class="Other pragmabegin">$1</span><div class="pragma">""",
+      dispA(d.conf, result, """<span class="Other pragmabegin">$1</span><div class="pragma">""",
                     "\\spanOther{$1}",
                   [rope(esc(d.target, literal))])
     of tkCurlyDotRi:
-      dispA(result, "</div><span class=\"Other pragmaend\">$1</span>",
+      dispA(d.conf, result, "</div><span class=\"Other pragmaend\">$1</span>",
                     "\\spanOther{$1}",
                   [rope(esc(d.target, literal))])
     of tkParLe, tkParRi, tkBracketLe, tkBracketRi, tkCurlyLe, tkCurlyRi,
@@ -259,7 +270,7 @@ proc nodeToHighlightedHtml(d: PDoc; n: PNode; result: var Rope; renderFlags: TRe
        tkAccent, tkColonColon,
        tkGStrLit, tkGTripleStrLit, tkInfixOpr, tkPrefixOpr, tkPostfixOpr,
        tkBracketLeColon:
-      dispA(result, "<span class=\"Other\">$1</span>", "\\spanOther{$1}",
+      dispA(d.conf, result, "<span class=\"Other\">$1</span>", "\\spanOther{$1}",
             [rope(esc(d.target, literal))])
 
 proc getAllRunnableExamples(d: PDoc; n: PNode; dest: var Rope) =
@@ -267,7 +278,7 @@ proc getAllRunnableExamples(d: PDoc; n: PNode; dest: var Rope) =
   of nkCallKinds:
     if n[0].kind == nkSym and n[0].sym.magic == mRunnableExamples and
         n.len >= 2 and n.lastSon.kind == nkStmtList:
-      dispA(dest, "\n<strong class=\"examples_text\">$1</strong>\n",
+      dispA(d.conf, dest, "\n<strong class=\"examples_text\">$1</strong>\n",
           "\n\\textbf{$1}\n", [rope"Examples:"])
       inc d.listingCounter
       let id = $d.listingCounter
@@ -336,7 +347,6 @@ proc getName(d: PDoc, n: PNode, splitAfter = -1): string =
   of nkOpenSymChoice, nkClosedSymChoice:
     result = getName(d, n[0], splitAfter)
   else:
-    internalError(n.info, "getName()")
     result = ""
 
 proc getNameIdent(n: PNode): PIdent =
@@ -366,7 +376,6 @@ proc getRstName(n: PNode): PRstNode =
   of nkOpenSymChoice, nkClosedSymChoice:
     result = getRstName(n[0])
   else:
-    internalError(n.info, "getRstName()")
     result = nil
 
 proc newUniquePlainSymbol(d: PDoc, original: string): string =
@@ -490,22 +499,22 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) =
     symbolOrIdEncRope = encodeUrl(symbolOrId).rope
 
   var seeSrcRope: Rope = nil
-  let docItemSeeSrc = getConfigVar("doc.item.seesrc")
+  let docItemSeeSrc = getConfigVar(d.conf, "doc.item.seesrc")
   if docItemSeeSrc.len > 0:
-    let cwd = getCurrentDir().canonicalizePath()
+    let cwd = canonicalizePath(d.conf, getCurrentDir())
     var path = n.info.toFullPath
     if path.startsWith(cwd):
       path = path[cwd.len+1 .. ^1].replace('\\', '/')
-    let gitUrl = getConfigVar("git.url")
+    let gitUrl = getConfigVar(d.conf, "git.url")
     if gitUrl.len > 0:
-      var commit = getConfigVar("git.commit")
+      var commit = getConfigVar(d.conf, "git.commit")
       if commit.len == 0: commit = "master"
-      dispA(seeSrcRope, "$1", "", [ropeFormatNamedVars(docItemSeeSrc,
+      dispA(d.conf, seeSrcRope, "$1", "", [ropeFormatNamedVars(d.conf, docItemSeeSrc,
           ["path", "line", "url", "commit"], [rope path,
           rope($n.info.line), rope gitUrl,
           rope commit])])
 
-  add(d.section[k], ropeFormatNamedVars(getConfigVar("doc.item"),
+  add(d.section[k], ropeFormatNamedVars(d.conf, getConfigVar(d.conf, "doc.item"),
     ["name", "header", "desc", "itemID", "header_plain", "itemSym",
       "itemSymOrID", "itemSymEnc", "itemSymOrIDEnc", "seeSrc"],
     [nameRope, result, comm, itemIDRope, plainNameRope, plainSymbolRope,
@@ -516,7 +525,7 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) =
     let att = attachToType(d, nameNode.sym)
     if att != nil:
       attype = rope esc(d.target, att.name.s)
-  add(d.toc[k], ropeFormatNamedVars(getConfigVar("doc.item.toc"),
+  add(d.toc[k], ropeFormatNamedVars(d.conf, getConfigVar(d.conf, "doc.item.toc"),
     ["name", "header", "desc", "itemID", "header_plain", "itemSym",
       "itemSymOrID", "itemSymEnc", "itemSymOrIDEnc", "attype"],
     [rope(getName(d, nameNode, d.splitAfter)), result, comm,
@@ -544,7 +553,7 @@ proc genJsonItem(d: PDoc, n, nameNode: PNode, k: TSymKind): JsonNode =
 
   initTokRender(r, n, {renderNoBody, renderNoComments, renderDocComments})
 
-  result = %{ "name": %name, "type": %($k), "line": %n.info.line,
+  result = %{ "name": %name, "type": %($k), "line": %n.info.line.int,
                  "col": %n.info.col}
   if comm != nil and comm != "":
     result["description"] = %comm
@@ -569,9 +578,9 @@ proc traceDeps(d: PDoc, it: PNode) =
       traceDeps(d, a)
   else:
     if d.section[k] != nil: add(d.section[k], ", ")
-    dispA(d.section[k],
+    dispA(d.conf, d.section[k],
           "<a class=\"reference external\" href=\"$1.html\">$1</a>",
-          "$1", [rope(getModuleName(it))])
+          "$1", [rope(getModuleName(d.conf, it))])
 
 proc generateDoc*(d: PDoc, n: PNode) =
   case n.kind
@@ -618,7 +627,7 @@ proc generateJson*(d: PDoc, n: PNode) =
   of nkCommentStmt:
     if n.comment != nil and startsWith(n.comment, "##"):
       let stripped = n.comment.substr(2).strip
-      d.add %{ "comment": %stripped, "line": %n.info.line,
+      d.add %{ "comment": %stripped, "line": %n.info.line.int,
                "col": %n.info.col }
   of nkProcDef:
     when useEffectSystem: documentRaises(n)
@@ -704,10 +713,10 @@ proc genSection(d: PDoc, kind: TSymKind) =
   ]
   if d.section[kind] == nil: return
   var title = sectionNames[kind].rope
-  d.section[kind] = ropeFormatNamedVars(getConfigVar("doc.section"), [
+  d.section[kind] = ropeFormatNamedVars(d.conf, getConfigVar(d.conf, "doc.section"), [
       "sectionid", "sectionTitle", "sectionTitleID", "content"], [
       ord(kind).rope, title, rope(ord(kind) + 50), d.section[kind]])
-  d.toc[kind] = ropeFormatNamedVars(getConfigVar("doc.section.toc"), [
+  d.toc[kind] = ropeFormatNamedVars(d.conf, getConfigVar(d.conf, "doc.section.toc"), [
       "sectionid", "sectionTitle", "sectionTitleID", "content"], [
       ord(kind).rope, title, rope(ord(kind) + 50), d.toc[kind]])
 
@@ -723,7 +732,7 @@ proc genOutFile(d: PDoc): Rope =
     genSection(d, i)
     add(toc, d.toc[i])
   if toc != nil:
-    toc = ropeFormatNamedVars(getConfigVar("doc.toc"), ["content"], [toc])
+    toc = ropeFormatNamedVars(d.conf, getConfigVar(d.conf, "doc.toc"), ["content"], [toc])
   for i in countup(low(TSymKind), high(TSymKind)): add(code, d.section[i])
 
   # Extract the title. Non API modules generate an entry in the index table.
@@ -737,13 +746,13 @@ proc genOutFile(d: PDoc): Rope =
   let bodyname = if d.hasToc and not d.isPureRst: "doc.body_toc_group"
                  elif d.hasToc: "doc.body_toc"
                  else: "doc.body_no_toc"
-  content = ropeFormatNamedVars(getConfigVar(bodyname), ["title",
+  content = ropeFormatNamedVars(d.conf, getConfigVar(d.conf, bodyname), ["title",
       "tableofcontents", "moduledesc", "date", "time", "content"],
       [title.rope, toc, d.modDesc, rope(getDateStr()),
       rope(getClockStr()), code])
-  if optCompileOnly notin gGlobalOptions:
+  if optCompileOnly notin d.conf.globalOptions:
     # XXX what is this hack doing here? 'optCompileOnly' means raw output!?
-    code = ropeFormatNamedVars(getConfigVar("doc.file"), ["title",
+    code = ropeFormatNamedVars(d.conf, getConfigVar(d.conf, "doc.file"), ["title",
         "tableofcontents", "moduledesc", "date", "time",
         "content", "author", "version", "analytics"],
         [title.rope, toc, d.modDesc, rope(getDateStr()),
@@ -754,60 +763,60 @@ proc genOutFile(d: PDoc): Rope =
   result = code
 
 proc generateIndex*(d: PDoc) =
-  if optGenIndex in gGlobalOptions:
-    writeIndexFile(d[], splitFile(options.outFile).dir /
+  if optGenIndex in d.conf.globalOptions:
+    writeIndexFile(d[], splitFile(d.conf.outFile).dir /
                         splitFile(d.filename).name & IndexExt)
 
-proc getOutFile2(filename, ext, dir: string): string =
-  if gWholeProject:
-    let d = if options.outFile != "": options.outFile else: dir
+proc getOutFile2(conf: ConfigRef; filename, ext, dir: string): string =
+  if optWholeProject in conf.globalOptions:
+    let d = if conf.outFile != "": conf.outFile else: dir
     createDir(d)
     result = d / changeFileExt(filename, ext)
   else:
-    result = getOutFile(filename, ext)
+    result = getOutFile(conf, filename, ext)
 
 proc writeOutput*(d: PDoc, filename, outExt: string, useWarning = false) =
   var content = genOutFile(d)
-  if optStdout in gGlobalOptions:
+  if optStdout in d.conf.globalOptions:
     writeRope(stdout, content)
   else:
-    writeRope(content, getOutFile2(filename, outExt, "htmldocs"), useWarning)
+    writeRope(content, getOutFile2(d.conf, filename, outExt, "htmldocs"), useWarning)
 
 proc writeOutputJson*(d: PDoc, filename, outExt: string,
                       useWarning = false) =
   let content = %*{"orig": d.filename,
-    "nimble": getPackageName(d.filename),
+    "nimble": getPackageName(d.conf, d.filename),
     "entries": d.jArray}
-  if optStdout in gGlobalOptions:
+  if optStdout in d.conf.globalOptions:
     write(stdout, $content)
   else:
     var f: File
-    if open(f, getOutFile2(splitFile(filename).name,
+    if open(f, getOutFile2(d.conf, splitFile(filename).name,
             outExt, "jsondocs"), fmWrite):
       write(f, $content)
       close(f)
     else:
       discard "fixme: error report"
 
-proc commandDoc*() =
-  var ast = parseFile(gProjectMainIdx, newIdentCache())
+proc commandDoc*(conf: ConfigRef) =
+  var ast = parseFile(conf.projectMainIdx.FileIndex, newIdentCache(), conf)
   if ast == nil: return
-  var d = newDocumentor(gProjectFull, options.gConfigVars)
+  var d = newDocumentor(conf.projectFull, conf)
   d.hasToc = true
   generateDoc(d, ast)
-  writeOutput(d, gProjectFull, HtmlExt)
+  writeOutput(d, conf.projectFull, HtmlExt)
   generateIndex(d)
 
-proc commandRstAux(filename, outExt: string) =
+proc commandRstAux(conf: ConfigRef; filename, outExt: string) =
   var filen = addFileExt(filename, "txt")
-  var d = newDocumentor(filen, options.gConfigVars)
+  var d = newDocumentor(filen, conf)
   d.onTestSnippet = proc (d: var RstGenerator; filename, cmd: string;
                           status: int; content: string) =
     var outp: string
     if filename.len == 0:
       inc(d.id)
       let nameOnly = splitFile(d.filename).name
-      let subdir = getNimcacheDir() / nameOnly
+      let subdir = getNimcacheDir(conf) / nameOnly
       createDir(subdir)
       outp = subdir / (nameOnly & "_snippet_" & $d.id & ".nim")
     elif isAbsolute(filename):
@@ -817,13 +826,13 @@ proc commandRstAux(filename, outExt: string) =
       outp = splitFile(d.filename).dir / filename
     writeFile(outp, content)
     let cmd = cmd % quoteShell(outp)
-    rawMessage(hintExecuting, cmd)
+    rawMessage(conf, hintExecuting, cmd)
     if execShellCmd(cmd) != status:
-      rawMessage(errExecutionOfProgramFailed, cmd)
+      rawMessage(conf, errGenerated, "executing of external program failed: " & cmd)
 
   d.isPureRst = true
   var rst = parseRst(readFile(filen), filen, 0, 1, d.hasToc,
-                     {roSupportRawDirective})
+                     {roSupportRawDirective}, conf)
   var modDesc = newStringOfCap(30_000)
   #d.modDesc = newMutableRope(30_000)
   renderRstToOut(d[], rst, modDesc)
@@ -832,50 +841,50 @@ proc commandRstAux(filename, outExt: string) =
   writeOutput(d, filename, outExt)
   generateIndex(d)
 
-proc commandRst2Html*() =
-  commandRstAux(gProjectFull, HtmlExt)
+proc commandRst2Html*(conf: ConfigRef) =
+  commandRstAux(conf, conf.projectFull, HtmlExt)
 
-proc commandRst2TeX*() =
+proc commandRst2TeX*(conf: ConfigRef) =
   splitter = "\\-"
-  commandRstAux(gProjectFull, TexExt)
+  commandRstAux(conf, conf.projectFull, TexExt)
 
-proc commandJson*() =
-  var ast = parseFile(gProjectMainIdx, newIdentCache())
+proc commandJson*(conf: ConfigRef) =
+  var ast = parseFile(conf.projectMainIdx.FileIndex, newIdentCache(), conf)
   if ast == nil: return
-  var d = newDocumentor(gProjectFull, options.gConfigVars)
+  var d = newDocumentor(conf.projectFull, conf)
   d.hasToc = true
   generateJson(d, ast)
   let json = d.jArray
   let content = rope(pretty(json))
 
-  if optStdout in gGlobalOptions:
+  if optStdout in d.conf.globalOptions:
     writeRope(stdout, content)
   else:
     #echo getOutFile(gProjectFull, JsonExt)
-    writeRope(content, getOutFile(gProjectFull, JsonExt), useWarning = false)
+    writeRope(content, getOutFile(conf, conf.projectFull, JsonExt), useWarning = false)
 
-proc commandTags*() =
-  var ast = parseFile(gProjectMainIdx, newIdentCache())
+proc commandTags*(conf: ConfigRef) =
+  var ast = parseFile(conf.projectMainIdx.FileIndex, newIdentCache(), conf)
   if ast == nil: return
-  var d = newDocumentor(gProjectFull, options.gConfigVars)
+  var d = newDocumentor(conf.projectFull, conf)
   d.hasToc = true
   var
     content: Rope
   generateTags(d, ast, content)
 
-  if optStdout in gGlobalOptions:
+  if optStdout in d.conf.globalOptions:
     writeRope(stdout, content)
   else:
     #echo getOutFile(gProjectFull, TagsExt)
-    writeRope(content, getOutFile(gProjectFull, TagsExt), useWarning = false)
+    writeRope(content, getOutFile(conf, conf.projectFull, TagsExt), useWarning = false)
 
-proc commandBuildIndex*() =
-  var content = mergeIndexes(gProjectFull).rope
+proc commandBuildIndex*(conf: ConfigRef) =
+  var content = mergeIndexes(conf.projectFull).rope
 
-  let code = ropeFormatNamedVars(getConfigVar("doc.file"), ["title",
+  let code = ropeFormatNamedVars(conf, getConfigVar(conf, "doc.file"), ["title",
       "tableofcontents", "moduledesc", "date", "time",
       "content", "author", "version", "analytics"],
       ["Index".rope, nil, nil, rope(getDateStr()),
                    rope(getClockStr()), content, nil, nil, nil])
   # no analytics because context is not available
-  writeRope(code, getOutFile("theindex", HtmlExt))
+  writeRope(code, getOutFile(conf, "theindex", HtmlExt))
diff --git a/compiler/docgen2.nim b/compiler/docgen2.nim
index 118f1c7c5..d9a73e1cd 100644
--- a/compiler/docgen2.nim
+++ b/compiler/docgen2.nim
@@ -25,8 +25,8 @@ 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 gWholeProject) or
-    sfMainModule in g.module.flags:
+  if (g.module.owner.id == gMainPackageId and optWholeProject in g.doc.conf.globalOptions) or
+      sfMainModule in g.module.flags:
     body
     try:
       generateIndex(g.doc)
@@ -55,7 +55,7 @@ proc myOpen(graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext =
   var g: PGen
   new(g)
   g.module = module
-  var d = newDocumentor(module.filename, options.gConfigVars)
+  var d = newDocumentor(module.filename, graph.config)
   d.hasToc = true
   g.doc = d
   result = g
diff --git a/compiler/evaltempl.nim b/compiler/evaltempl.nim
index 704ff819c..01c56ec9c 100644
--- a/compiler/evaltempl.nim
+++ b/compiler/evaltempl.nim
@@ -14,11 +14,12 @@ import
   rodread
 
 type
-  TemplCtx {.pure, final.} = object
+  TemplCtx = object
     owner, genSymOwner: PSym
     instLines: bool   # use the instantiation lines numbers
     mapping: TIdTable # every gensym'ed symbol needs to be mapped to some
                       # new symbol
+    config: ConfigRef
 
 proc copyNode(ctx: TemplCtx, a, b: PNode): PNode =
   result = copyNode(a)
@@ -42,7 +43,7 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
            s.kind == skType and s.typ != nil and s.typ.kind == tyGenericParam:
         handleParam actual.sons[s.owner.typ.len + s.position - 1]
       else:
-        internalAssert sfGenSym in s.flags or s.kind == skType
+        internalAssert c.config, sfGenSym in s.flags or s.kind == skType
         var x = PSym(idTableGet(c.mapping, s))
         if x == nil:
           x = copySym(s, false)
@@ -59,11 +60,16 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
       evalTemplateAux(templ.sons[i], actual, c, res)
     result.add res
 
-proc evalTemplateArgs(n: PNode, s: PSym; fromHlo: bool): PNode =
+const
+  errWrongNumberOfArguments = "wrong number of arguments"
+  errMissingGenericParamsForTemplate = "'$1' has unspecified generic parameters"
+  errTemplateInstantiationTooNested = "template instantiation too nested"
+
+proc evalTemplateArgs(n: PNode, s: PSym; conf: ConfigRef; fromHlo: bool): PNode =
   # if the template has zero arguments, it can be called without ``()``
   # `n` is then a nkSym or something similar
   var totalParams = case n.kind
-    of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit: n.len-1
+    of nkCallKinds: n.len-1
     else: 0
 
   var
@@ -82,10 +88,10 @@ proc evalTemplateArgs(n: PNode, s: PSym; fromHlo: bool): PNode =
   if givenRegularParams < 0: givenRegularParams = 0
 
   if totalParams > expectedRegularParams + genericParams:
-    globalError(n.info, errWrongNumberOfArguments)
+    globalError(conf, n.info, errWrongNumberOfArguments)
 
   if totalParams < genericParams:
-    globalError(n.info, errMissingGenericParamsForTemplate,
+    globalError(conf, n.info, errMissingGenericParamsForTemplate %
                 n.renderTree)
 
   result = newNodeI(nkArgList, n.info)
@@ -97,7 +103,7 @@ proc evalTemplateArgs(n: PNode, s: PSym; fromHlo: bool): PNode =
   for i in givenRegularParams+1 .. expectedRegularParams:
     let default = s.typ.n.sons[i].sym.ast
     if default.isNil or default.kind == nkEmpty:
-      localError(n.info, errWrongNumberOfArguments)
+      localError(conf, n.info, errWrongNumberOfArguments)
       addSon(result, ast.emptyNode)
     else:
       addSon(result, default.copyTree)
@@ -106,8 +112,9 @@ proc evalTemplateArgs(n: PNode, s: PSym; fromHlo: bool): PNode =
   for i in 1 .. genericParams:
     result.addSon n.sons[givenRegularParams + i]
 
-var evalTemplateCounter* = 0
-  # to prevent endless recursion in templates instantiation
+# 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:
@@ -131,17 +138,19 @@ proc wrapInComesFrom*(info: TLineInfo; sym: PSym; res: PNode): PNode =
     result.add res
     result.typ = res.typ
 
-proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym; fromHlo=false): PNode =
+proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym;
+                   conf: ConfigRef; fromHlo=false): PNode =
   inc(evalTemplateCounter)
-  if evalTemplateCounter > 100:
-    globalError(n.info, errTemplateInstantiationTooNested)
+  if evalTemplateCounter > evalTemplateLimit:
+    globalError(conf, n.info, errTemplateInstantiationTooNested)
     result = n
 
   # replace each param by the corresponding node:
-  var args = evalTemplateArgs(n, tmpl, fromHlo)
+  var args = evalTemplateArgs(n, tmpl, conf, fromHlo)
   var ctx: TemplCtx
   ctx.owner = tmpl
   ctx.genSymOwner = genSymOwner
+  ctx.config = conf
   initIdTable(ctx.mapping)
 
   let body = tmpl.getBody
@@ -150,7 +159,7 @@ proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym; fromHlo=false): PNode =
     evalTemplateAux(body, args, ctx, result)
     if result.len == 1: result = result.sons[0]
     else:
-      localError(result.info, errIllFormedAstX,
+      localError(conf, result.info, "illformed AST: " &
                   renderTree(result, {renderNoComments}))
   else:
     result = copyNode(body)
diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim
index f9b18a335..3f0e6f611 100644
--- a/compiler/extccomp.nim
+++ b/compiler/extccomp.nim
@@ -14,7 +14,7 @@
 
 import
   ropes, os, strutils, osproc, platform, condsyms, options, msgs,
-  std / sha1, streams
+  configuration, std / sha1, streams
 
 #from debuginfo import writeDebugInfo
 
@@ -193,9 +193,9 @@ compiler bcc:
     pic: "",
     asmStmtFrmt: "__asm{$n$1$n}$n",
     structStmtFmt: "$1 $2",
-    props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard, 
+    props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard,
             hasAttribute})
-    
+
 
 # Digital Mars C Compiler
 compiler dmc:
@@ -376,80 +376,81 @@ proc nameToCC*(name: string): TSystemCC =
       return i
   result = ccNone
 
-proc getConfigVar(c: TSystemCC, suffix: string): string =
+proc getConfigVar(conf: ConfigRef; c: TSystemCC, suffix: string): string =
   # use ``cpu.os.cc`` for cross compilation, unless ``--compileOnly`` is given
   # for niminst support
   let fullSuffix =
-    if gCmd == cmdCompileToCpp:
+    if conf.cmd == cmdCompileToCpp:
       ".cpp" & suffix
-    elif gCmd == cmdCompileToOC:
+    elif conf.cmd == cmdCompileToOC:
       ".objc" & suffix
-    elif gCmd == cmdCompileToJS:
+    elif conf.cmd == cmdCompileToJS:
       ".js" & suffix
     else:
       suffix
 
   if (platform.hostOS != targetOS or platform.hostCPU != targetCPU) and
-      optCompileOnly notin gGlobalOptions:
+      optCompileOnly notin conf.globalOptions:
     let fullCCname = platform.CPU[targetCPU].name & '.' &
                      platform.OS[targetOS].name & '.' &
                      CC[c].name & fullSuffix
-    result = getConfigVar(fullCCname)
+    result = getConfigVar(conf, fullCCname)
     if result.len == 0:
       # not overriden for this cross compilation setting?
-      result = getConfigVar(CC[c].name & fullSuffix)
+      result = getConfigVar(conf, CC[c].name & fullSuffix)
   else:
-    result = getConfigVar(CC[c].name & fullSuffix)
+    result = getConfigVar(conf, CC[c].name & fullSuffix)
 
-proc setCC*(ccname: string) =
+proc setCC*(conf: ConfigRef; ccname: string; info: TLineInfo) =
   cCompiler = nameToCC(ccname)
-  if cCompiler == ccNone: rawMessage(errUnknownCcompiler, ccname)
-  compileOptions = getConfigVar(cCompiler, ".options.always")
+  if cCompiler == ccNone:
+    localError(conf, info, "unknown C compiler: '$1'" % ccname)
+  compileOptions = getConfigVar(conf, cCompiler, ".options.always")
   linkOptions = ""
-  ccompilerpath = getConfigVar(cCompiler, ".path")
-  for i in countup(low(CC), high(CC)): undefSymbol(CC[i].name)
-  defineSymbol(CC[cCompiler].name)
+  ccompilerpath = getConfigVar(conf, cCompiler, ".path")
+  for i in countup(low(CC), high(CC)): undefSymbol(conf.symbols, CC[i].name)
+  defineSymbol(conf.symbols, CC[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*(option: string) =
+proc addLinkOption*(conf: ConfigRef; option: string) =
   addOpt(linkOptions, option)
 
-proc addCompileOption*(option: string) =
+proc addCompileOption*(conf: ConfigRef; option: string) =
   if strutils.find(compileOptions, option, 0) < 0:
     addOpt(compileOptions, option)
 
-proc addLinkOptionCmd*(option: string) =
+proc addLinkOptionCmd*(conf: ConfigRef; option: string) =
   addOpt(linkOptionsCmd, option)
 
-proc addCompileOptionCmd*(option: string) =
+proc addCompileOptionCmd*(conf: ConfigRef; option: string) =
   compileOptionsCmd.add(option)
 
-proc initVars*() =
+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(CC[i].name)
-  defineSymbol(CC[cCompiler].name)
-  addCompileOption(getConfigVar(cCompiler, ".options.always"))
+  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"))
   #addLinkOption(getConfigVar(cCompiler, ".options.linker"))
   if len(ccompilerpath) == 0:
-    ccompilerpath = getConfigVar(cCompiler, ".path")
+    ccompilerpath = getConfigVar(conf, cCompiler, ".path")
 
-proc completeCFilePath*(cfile: string, createSubDir: bool = true): string =
-  result = completeGeneratedFilePath(cfile, createSubDir)
+proc completeCFilePath*(conf: ConfigRef; cfile: string, createSubDir: bool = true): string =
+  result = completeGeneratedFilePath(conf, cfile, createSubDir)
 
-proc toObjFile*(filename: string): string =
+proc toObjFile*(conf: ConfigRef; filename: string): string =
   # Object file for compilation
   #if filename.endsWith(".cpp"):
   #  result = changeFileExt(filename, "cpp." & CC[cCompiler].objExt)
   #else:
   result = changeFileExt(filename, CC[cCompiler].objExt)
 
-proc addFileToCompile*(cf: Cfile) =
+proc addFileToCompile*(conf: ConfigRef; cf: Cfile) =
   toCompile.add(cf)
 
-proc resetCompilationLists* =
+proc resetCompilationLists*(conf: ConfigRef) =
   toCompile.setLen 0
   ## XXX: we must associate these with their originating module
   # when the module is loaded/unloaded it adds/removes its items
@@ -457,108 +458,112 @@ proc resetCompilationLists* =
   # Maybe we can do that in checkDep on the other hand?
   externalToLink.setLen 0
 
-proc addExternalFileToLink*(filename: string) =
+proc addExternalFileToLink*(conf: ConfigRef; filename: string) =
   externalToLink.insert(filename, 0)
 
-proc execWithEcho(cmd: string, msg = hintExecuting): int =
-  rawMessage(msg, cmd)
+proc execWithEcho(conf: ConfigRef; cmd: string, msg = hintExecuting): int =
+  rawMessage(conf, msg, cmd)
   result = execCmd(cmd)
 
-proc execExternalProgram*(cmd: string, msg = hintExecuting) =
-  if execWithEcho(cmd, msg) != 0:
-    rawMessage(errExecutionOfProgramFailed, cmd)
+proc execExternalProgram*(conf: ConfigRef; cmd: string, msg = hintExecuting) =
+  if execWithEcho(conf, cmd, msg) != 0:
+    rawMessage(conf, errGenerated, "execution of an external program failed: '$1'" %
+      cmd)
 
-proc generateScript(projectFile: string, script: Rope) =
+proc generateScript(conf: ConfigRef; projectFile: string, script: Rope) =
   let (dir, name, ext) = splitFile(projectFile)
-  writeRope(script, dir / addFileExt("compile_" & name,
+  writeRope(script, getNimcacheDir(conf) / addFileExt("compile_" & name,
                                      platform.OS[targetOS].scriptExt))
+  copyFile(conf.libpath / "nimbase.h", getNimcacheDir(conf) / "nimbase.h")
 
-proc getOptSpeed(c: TSystemCC): string =
-  result = getConfigVar(c, ".options.speed")
+proc getOptSpeed(conf: ConfigRef; c: TSystemCC): string =
+  result = getConfigVar(conf, c, ".options.speed")
   if result == "":
     result = CC[c].optSpeed   # use default settings from this file
 
-proc getDebug(c: TSystemCC): string =
-  result = getConfigVar(c, ".options.debug")
+proc getDebug(conf: ConfigRef; c: TSystemCC): string =
+  result = getConfigVar(conf, c, ".options.debug")
   if result == "":
     result = CC[c].debug      # use default settings from this file
 
-proc getOptSize(c: TSystemCC): string =
-  result = getConfigVar(c, ".options.size")
+proc getOptSize(conf: ConfigRef; c: TSystemCC): string =
+  result = getConfigVar(conf, c, ".options.size")
   if result == "":
     result = CC[c].optSize    # use default settings from this file
 
-proc noAbsolutePaths: bool {.inline.} =
+proc noAbsolutePaths(conf: ConfigRef): bool {.inline.} =
   # We used to check current OS != specified OS, but this makes no sense
   # really: Cross compilation from Linux to Linux for example is entirely
   # reasonable.
   # `optGenMapping` is included here for niminst.
-  result = gGlobalOptions * {optGenScript, optGenMapping} != {}
+  result = conf.globalOptions * {optGenScript, optGenMapping} != {}
 
-proc cFileSpecificOptions(cfilename: string): string =
+proc cFileSpecificOptions(conf: ConfigRef; cfilename: string): string =
   result = compileOptions
   for option in compileOptionsCmd:
     if strutils.find(result, option, 0) < 0:
       addOpt(result, option)
 
-  var trunk = splitFile(cfilename).name
-  if optCDebug in gGlobalOptions:
-    var key = trunk & ".debug"
-    if existsConfigVar(key): addOpt(result, getConfigVar(key))
-    else: addOpt(result, getDebug(cCompiler))
-  if optOptimizeSpeed in gOptions:
-    var key = trunk & ".speed"
-    if existsConfigVar(key): addOpt(result, getConfigVar(key))
-    else: addOpt(result, getOptSpeed(cCompiler))
-  elif optOptimizeSize in gOptions:
-    var key = trunk & ".size"
-    if existsConfigVar(key): addOpt(result, getConfigVar(key))
-    else: addOpt(result, getOptSize(cCompiler))
-  var key = trunk & ".always"
-  if existsConfigVar(key): addOpt(result, getConfigVar(key))
-
-proc getCompileOptions: string =
-  result = cFileSpecificOptions("__dummy__")
-
-proc getLinkOptions: string =
+  let trunk = splitFile(cfilename).name
+  if optCDebug in conf.globalOptions:
+    let key = trunk & ".debug"
+    if existsConfigVar(conf, key): addOpt(result, getConfigVar(conf, key))
+    else: addOpt(result, getDebug(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))
+  elif optOptimizeSize in conf.options:
+    let key = trunk & ".size"
+    if existsConfigVar(conf, key): addOpt(result, getConfigVar(conf, key))
+    else: addOpt(result, getOptSize(conf, cCompiler))
+  let key = trunk & ".always"
+  if existsConfigVar(conf, key): addOpt(result, getConfigVar(conf, key))
+
+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]))
 
-proc needsExeExt(): bool {.inline.} =
-  result = (optGenScript in gGlobalOptions and targetOS == osWindows) or
+proc needsExeExt(conf: ConfigRef): bool {.inline.} =
+  result = (optGenScript in conf.globalOptions and targetOS == osWindows) or
            (platform.hostOS == osWindows)
 
-proc getCompilerExe(compiler: TSystemCC; cfile: string): string =
-  result = if gCmd == cmdCompileToCpp and not cfile.endsWith(".c"):
+proc getCompilerExe(conf: ConfigRef; compiler: TSystemCC; cfile: string): string =
+  result = if conf.cmd == cmdCompileToCpp and not cfile.endsWith(".c"):
              CC[compiler].cppCompiler
            else:
              CC[compiler].compilerExe
   if result.len == 0:
-    rawMessage(errCompilerDoesntSupportTarget, CC[compiler].name)
+    rawMessage(conf, errGenerated,
+      "Compiler '$1' doesn't support the requested target" %
+      CC[compiler].name)
 
-proc getLinkerExe(compiler: TSystemCC): string =
+proc getLinkerExe(conf: ConfigRef; compiler: TSystemCC): string =
   result = if CC[compiler].linkerExe.len > 0: CC[compiler].linkerExe
-           elif gMixedMode and gCmd != cmdCompileToCpp: CC[compiler].cppCompiler
-           else: compiler.getCompilerExe("")
+           elif gMixedMode and conf.cmd != cmdCompileToCpp: CC[compiler].cppCompiler
+           else: getCompilerExe(conf, compiler, "")
 
-proc getCompileCFileCmd*(cfile: Cfile): string =
+proc getCompileCFileCmd*(conf: ConfigRef; cfile: Cfile): string =
   var c = cCompiler
-  var options = cFileSpecificOptions(cfile.cname)
-  var exe = getConfigVar(c, ".exe")
-  if exe.len == 0: exe = c.getCompilerExe(cfile.cname)
+  var options = cFileSpecificOptions(conf, cfile.cname)
+  var exe = getConfigVar(conf, c, ".exe")
+  if exe.len == 0: exe = getCompilerExe(conf, c, cfile.cname)
 
-  if needsExeExt(): exe = addFileExt(exe, "exe")
-  if optGenDynLib in gGlobalOptions and
+  if needsExeExt(conf): exe = addFileExt(exe, "exe")
+  if optGenDynLib in conf.globalOptions and
       ospNeedsPIC in platform.OS[targetOS].props:
     add(options, ' ' & CC[c].pic)
 
   var includeCmd, compilePattern: string
-  if not noAbsolutePaths():
+  if not noAbsolutePaths(conf):
     # compute include paths:
-    includeCmd = CC[c].includeCmd & quoteShell(libpath)
+    includeCmd = CC[c].includeCmd & quoteShell(conf.libpath)
 
     for includeDir in items(cIncludes):
       includeCmd.add(join([CC[c].includeCmd, includeDir.quoteShell]))
@@ -566,18 +571,18 @@ proc getCompileCFileCmd*(cfile: Cfile): string =
     compilePattern = joinPath(ccompilerpath, exe)
   else:
     includeCmd = ""
-    compilePattern = c.getCompilerExe(cfile.cname)
+    compilePattern = getCompilerExe(conf, c, cfile.cname)
 
-  var cf = if noAbsolutePaths(): extractFilename(cfile.cname)
+  var cf = if noAbsolutePaths(conf): extractFilename(cfile.cname)
            else: cfile.cname
 
   var objfile =
     if cfile.obj.len == 0:
-      if not cfile.flags.contains(CfileFlag.External) or noAbsolutePaths():
-        toObjFile(cf)
+      if not cfile.flags.contains(CfileFlag.External) or noAbsolutePaths(conf):
+        toObjFile(conf, cf)
       else:
-        completeCFilePath(toObjFile(cf))
-    elif noAbsolutePaths():
+        completeCFilePath(conf, toObjFile(conf, cf))
+    elif noAbsolutePaths(conf):
       extractFilename(cfile.obj)
     else:
       cfile.obj
@@ -586,30 +591,30 @@ proc getCompileCFileCmd*(cfile: Cfile): string =
   cf = quoteShell(cf)
   result = quoteShell(compilePattern % [
     "file", cf, "objfile", objfile, "options", options,
-    "include", includeCmd, "nim", getPrefixDir(),
-    "nim", getPrefixDir(), "lib", libpath])
+    "include", includeCmd, "nim", getPrefixDir(conf),
+    "nim", getPrefixDir(conf), "lib", conf.libpath])
   add(result, ' ')
   addf(result, CC[c].compileTmpl, [
     "file", cf, "objfile", objfile,
     "options", options, "include", includeCmd,
-    "nim", quoteShell(getPrefixDir()),
-    "nim", quoteShell(getPrefixDir()),
-    "lib", quoteShell(libpath)])
+    "nim", quoteShell(getPrefixDir(conf)),
+    "nim", quoteShell(getPrefixDir(conf)),
+    "lib", quoteShell(conf.libpath)])
 
-proc footprint(cfile: Cfile): SecureHash =
+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 &
-    getCompileCFileCmd(cfile))
+    getCompileCFileCmd(conf, cfile))
 
-proc externalFileChanged(cfile: Cfile): bool =
-  if gCmd notin {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToLLVM}:
+proc externalFileChanged(conf: ConfigRef; cfile: Cfile): bool =
+  if conf.cmd notin {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToLLVM}:
     return false
 
-  var hashFile = toGeneratedFile(cfile.cname.withPackageName, "sha1")
-  var currentHash = footprint(cfile)
+  var hashFile = toGeneratedFile(conf, conf.withPackageName(cfile.cname), "sha1")
+  var currentHash = footprint(conf, cfile)
   var f: File
   if open(f, hashFile, fmRead):
     let oldHash = parseSecureHash(f.readLine())
@@ -622,133 +627,137 @@ proc externalFileChanged(cfile: Cfile): bool =
       f.writeLine($currentHash)
       close(f)
 
-proc addExternalFileToCompile*(c: var Cfile) =
-  if optForceFullMake notin gGlobalOptions and not externalFileChanged(c):
+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)
 
-proc addExternalFileToCompile*(filename: string) =
+proc addExternalFileToCompile*(conf: ConfigRef; filename: string) =
   var c = Cfile(cname: filename,
-    obj: toObjFile(completeCFilePath(changeFileExt(filename, ""), false)),
+    obj: toObjFile(conf, completeCFilePath(conf, changeFileExt(filename, ""), false)),
     flags: {CfileFlag.External})
-  addExternalFileToCompile(c)
+  addExternalFileToCompile(conf, c)
 
-proc compileCFile(list: CFileList, script: var Rope, cmds: var TStringSeq,
+proc compileCFile(conf: ConfigRef; list: CFileList, script: var Rope, cmds: var TStringSeq,
                   prettyCmds: var TStringSeq) =
   for it in list:
     # call the C compiler for the .c file:
     if it.flags.contains(CfileFlag.Cached): continue
-    var compileCmd = getCompileCFileCmd(it)
-    if optCompileOnly notin gGlobalOptions:
+    var compileCmd = getCompileCFileCmd(conf, it)
+    if optCompileOnly notin conf.globalOptions:
       add(cmds, compileCmd)
       let (_, name, _) = splitFile(it.cname)
       add(prettyCmds, "CC: " & name)
-    if optGenScript in gGlobalOptions:
+    if optGenScript in conf.globalOptions:
       add(script, compileCmd)
       add(script, tnl)
 
-proc getLinkCmd(projectfile, objfiles: string): string =
-  if optGenStaticLib in gGlobalOptions:
+proc getLinkCmd(conf: ConfigRef; projectfile, objfiles: string): string =
+  if optGenStaticLib in conf.globalOptions:
     var libname: string
-    if options.outFile.len > 0:
-      libname = options.outFile.expandTilde
+    if conf.outFile.len > 0:
+      libname = conf.outFile.expandTilde
       if not libname.isAbsolute():
         libname = getCurrentDir() / libname
     else:
-      libname = (libNameTmpl() % splitFile(gProjectName).name)
+      libname = (libNameTmpl() % splitFile(conf.projectName).name)
     result = CC[cCompiler].buildLib % ["libfile", libname,
                                        "objfiles", objfiles]
   else:
-    var linkerExe = getConfigVar(cCompiler, ".linkerexe")
-    if len(linkerExe) == 0: linkerExe = cCompiler.getLinkerExe
+    var linkerExe = getConfigVar(conf, cCompiler, ".linkerexe")
+    if len(linkerExe) == 0: linkerExe = getLinkerExe(conf, cCompiler)
     # bug #6452: We must not use ``quoteShell`` here for ``linkerExe``
-    if needsExeExt(): linkerExe = addFileExt(linkerExe, "exe")
-    if noAbsolutePaths(): result = linkerExe
+    if needsExeExt(conf): linkerExe = addFileExt(linkerExe, "exe")
+    if noAbsolutePaths(conf): result = linkerExe
     else: result = joinPath(ccompilerpath, linkerExe)
-    let buildgui = if optGenGuiApp in gGlobalOptions: CC[cCompiler].buildGui
+    let buildgui = if optGenGuiApp in conf.globalOptions: CC[cCompiler].buildGui
                    else: ""
     var exefile, builddll: string
-    if optGenDynLib in gGlobalOptions:
+    if optGenDynLib in conf.globalOptions:
       exefile = platform.OS[targetOS].dllFrmt % splitFile(projectfile).name
       builddll = CC[cCompiler].buildDll
     else:
       exefile = splitFile(projectfile).name & platform.OS[targetOS].exeExt
       builddll = ""
-    if options.outFile.len > 0:
-      exefile = options.outFile.expandTilde
+    if conf.outFile.len > 0:
+      exefile = conf.outFile.expandTilde
       if not exefile.isAbsolute():
         exefile = getCurrentDir() / exefile
-    if not noAbsolutePaths():
+    if not noAbsolutePaths(conf):
       if not exefile.isAbsolute():
         exefile = joinPath(splitFile(projectfile).dir, exefile)
     when false:
-      if optCDebug in gGlobalOptions:
+      if optCDebug in conf.globalOptions:
         writeDebugInfo(exefile.changeFileExt("ndb"))
     exefile = quoteShell(exefile)
-    let linkOptions = getLinkOptions() & " " &
-                      getConfigVar(cCompiler, ".options.linker")
-    var linkTmpl = getConfigVar(cCompiler, ".linkTmpl")
+    let linkOptions = getLinkOptions(conf) & " " &
+                      getConfigVar(conf, cCompiler, ".options.linker")
+    var linkTmpl = getConfigVar(conf, cCompiler, ".linkTmpl")
     if linkTmpl.len == 0:
       linkTmpl = CC[cCompiler].linkTmpl
     result = quoteShell(result % ["builddll", builddll,
         "buildgui", buildgui, "options", linkOptions, "objfiles", objfiles,
-        "exefile", exefile, "nim", getPrefixDir(), "lib", libpath])
+        "exefile", exefile, "nim", getPrefixDir(conf), "lib", conf.libpath])
     result.add ' '
     addf(result, linkTmpl, ["builddll", builddll,
         "buildgui", buildgui, "options", linkOptions,
         "objfiles", objfiles, "exefile", exefile,
-        "nim", quoteShell(getPrefixDir()),
-        "lib", quoteShell(libpath)])
+        "nim", quoteShell(getPrefixDir(conf)),
+        "lib", quoteShell(conf.libpath)])
 
-template tryExceptOSErrorMessage(errorPrefix: string = "", body: untyped): typed =
+template tryExceptOSErrorMessage(conf: ConfigRef; errorPrefix: string = "", body: untyped): typed =
   try:
     body
   except OSError:
     let ose = (ref OSError)(getCurrentException())
     if errorPrefix.len > 0:
-      rawMessage(errGenerated, errorPrefix & " " & ose.msg & " " & $ose.errorCode)
+      rawMessage(conf, errGenerated, errorPrefix & " " & ose.msg & " " & $ose.errorCode)
     else:
-      rawMessage(errExecutionOfProgramFailed, ose.msg & " " & $ose.errorCode)
+      rawMessage(conf, errGenerated, "execution of an external program failed: '$1'" %
+        (ose.msg & " " & $ose.errorCode))
     raise
 
-proc execLinkCmd(linkCmd: string) =
-  tryExceptOSErrorMessage("invocation of external linker program failed."):
-    execExternalProgram(linkCmd,
-      if optListCmd in gGlobalOptions or gVerbosity > 1: hintExecuting else: hintLinking)
+proc execLinkCmd(conf: ConfigRef; linkCmd: string) =
+  tryExceptOSErrorMessage(conf, "invocation of external linker program failed."):
+    execExternalProgram(conf, linkCmd,
+      if optListCmd in conf.globalOptions or conf.verbosity > 1: hintExecuting else: hintLinking)
 
-proc execCmdsInParallel(cmds: seq[string]; prettyCb: proc (idx: int)) =
+proc execCmdsInParallel(conf: ConfigRef; cmds: seq[string]; prettyCb: proc (idx: int)) =
   let runCb = proc (idx: int, p: Process) =
     let exitCode = p.peekExitCode
     if exitCode != 0:
-      rawMessage(errGenerated, "execution of an external compiler program '" &
+      rawMessage(conf, errGenerated, "execution of an external compiler program '" &
         cmds[idx] & "' failed with exit code: " & $exitCode & "\n\n" &
         p.outputStream.readAll.strip)
-  if gNumberOfProcessors == 0: gNumberOfProcessors = countProcessors()
+  if conf.numberOfProcessors == 0: conf.numberOfProcessors = countProcessors()
   var res = 0
-  if gNumberOfProcessors <= 1:
+  if conf.numberOfProcessors <= 1:
     for i in countup(0, high(cmds)):
-      tryExceptOSErrorMessage("invocation of external compiler program failed."):
-        res = execWithEcho(cmds[i])
-      if res != 0: rawMessage(errExecutionOfProgramFailed, cmds[i])
+      tryExceptOSErrorMessage(conf, "invocation of external compiler program failed."):
+        res = execWithEcho(conf, cmds[i])
+      if res != 0:
+        rawMessage(conf, errGenerated, "execution of an external program failed: '$1'" %
+          cmds[i])
   else:
-    tryExceptOSErrorMessage("invocation of external compiler program failed."):
-      if optListCmd in gGlobalOptions or gVerbosity > 1:
+    tryExceptOSErrorMessage(conf, "invocation of external compiler program failed."):
+      if optListCmd in conf.globalOptions or conf.verbosity > 1:
         res = execProcesses(cmds, {poEchoCmd, poStdErrToStdOut, poUsePath},
-                            gNumberOfProcessors, afterRunEvent=runCb)
-      elif gVerbosity == 1:
+                            conf.numberOfProcessors, afterRunEvent=runCb)
+      elif conf.verbosity == 1:
         res = execProcesses(cmds, {poStdErrToStdOut, poUsePath},
-                            gNumberOfProcessors, prettyCb, afterRunEvent=runCb)
+                            conf.numberOfProcessors, prettyCb, afterRunEvent=runCb)
       else:
         res = execProcesses(cmds, {poStdErrToStdOut, poUsePath},
-                            gNumberOfProcessors, afterRunEvent=runCb)
+                            conf.numberOfProcessors, afterRunEvent=runCb)
   if res != 0:
-    if gNumberOfProcessors <= 1:
-      rawMessage(errExecutionOfProgramFailed, cmds.join())
+    if conf.numberOfProcessors <= 1:
+      rawMessage(conf, errGenerated, "execution of an external program failed: '$1'" %
+        cmds.join())
 
-proc callCCompiler*(projectfile: string) =
+proc callCCompiler*(conf: ConfigRef; projectfile: string) =
   var
     linkCmd: string
-  if gGlobalOptions * {optCompileOnly, optGenScript} == {optCompileOnly}:
+  if conf.globalOptions * {optCompileOnly, optGenScript} == {optCompileOnly}:
     return # speed up that call if only compiling and no script shall be
            # generated
   #var c = cCompiler
@@ -757,36 +766,36 @@ proc callCCompiler*(projectfile: string) =
   var prettyCmds: TStringSeq = @[]
   let prettyCb = proc (idx: int) =
     echo prettyCmds[idx]
-  compileCFile(toCompile, script, cmds, prettyCmds)
-  if optCompileOnly notin gGlobalOptions:
-    execCmdsInParallel(cmds, prettyCb)
-  if optNoLinking notin gGlobalOptions:
+  compileCFile(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:
-      let objFile = if noAbsolutePaths(): it.extractFilename else: it
+      let objFile = if noAbsolutePaths(conf): it.extractFilename else: it
       add(objfiles, ' ')
       add(objfiles, quoteShell(
           addFileExt(objFile, CC[cCompiler].objExt)))
     for x in toCompile:
-      let objFile = if noAbsolutePaths(): x.obj.extractFilename else: x.obj
+      let objFile = if noAbsolutePaths(conf): x.obj.extractFilename else: x.obj
       add(objfiles, ' ')
       add(objfiles, quoteShell(objFile))
 
-    linkCmd = getLinkCmd(projectfile, objfiles)
-    if optCompileOnly notin gGlobalOptions:
-      execLinkCmd(linkCmd)
+    linkCmd = getLinkCmd(conf, projectfile, objfiles)
+    if optCompileOnly notin conf.globalOptions:
+      execLinkCmd(conf, linkCmd)
   else:
     linkCmd = ""
-  if optGenScript in gGlobalOptions:
+  if optGenScript in conf.globalOptions:
     add(script, linkCmd)
     add(script, tnl)
-    generateScript(projectfile, script)
+    generateScript(conf, projectfile, script)
 
 #from json import escapeJson
 import json
 
-proc writeJsonBuildInstructions*(projectfile: string) =
+proc writeJsonBuildInstructions*(conf: ConfigRef; projectfile: string) =
   template lit(x: untyped) = f.write x
   template str(x: untyped) =
     when compiles(escapeJson(x, buf)):
@@ -796,11 +805,11 @@ proc writeJsonBuildInstructions*(projectfile: string) =
     else:
       f.write escapeJson(x)
 
-  proc cfiles(f: File; buf: var string; clist: CfileList, isExternal: bool) =
+  proc cfiles(conf: ConfigRef; f: File; buf: var string; clist: CfileList, isExternal: bool) =
     var pastStart = false
     for it in clist:
       if CfileFlag.Cached in it.flags: continue
-      let compileCmd = getCompileCFileCmd(it)
+      let compileCmd = getCompileCFileCmd(conf, it)
       if pastStart: lit "],\L"
       lit "["
       str it.cname
@@ -809,11 +818,11 @@ proc writeJsonBuildInstructions*(projectfile: string) =
       pastStart = true
     lit "]\L"
 
-  proc linkfiles(f: File; buf, objfiles: var string; clist: CfileList;
+  proc linkfiles(conf: ConfigRef; f: File; buf, objfiles: var string; clist: CfileList;
                  llist: seq[string]) =
     var pastStart = false
     for it in llist:
-      let objfile = if noAbsolutePaths(): it.extractFilename
+      let objfile = if noAbsolutePaths(conf): it.extractFilename
                     else: it
       let objstr = addFileExt(objfile, CC[cCompiler].objExt)
       add(objfiles, ' ')
@@ -834,25 +843,25 @@ proc writeJsonBuildInstructions*(projectfile: string) =
   var buf = newStringOfCap(50)
 
   let file = projectfile.splitFile.name
-  let jsonFile = toGeneratedFile(file, "json")
+  let jsonFile = toGeneratedFile(conf, file, "json")
 
   var f: File
   if open(f, jsonFile, fmWrite):
     lit "{\"compile\":[\L"
-    cfiles(f, buf, toCompile, false)
+    cfiles(conf, f, buf, toCompile, false)
     lit "],\L\"link\":[\L"
     var objfiles = ""
     # XXX add every file here that is to link
-    linkfiles(f, buf, objfiles, toCompile, externalToLink)
+    linkfiles(conf, f, buf, objfiles, toCompile, externalToLink)
 
     lit "],\L\"linkcmd\": "
-    str getLinkCmd(projectfile, objfiles)
+    str getLinkCmd(conf, projectfile, objfiles)
     lit "\L}\L"
     close(f)
 
-proc runJsonBuildInstructions*(projectfile: string) =
+proc runJsonBuildInstructions*(conf: ConfigRef; projectfile: string) =
   let file = projectfile.splitFile.name
-  let jsonFile = toGeneratedFile(file, "json")
+  let jsonFile = toGeneratedFile(conf, file, "json")
   try:
     let data = json.parseFile(jsonFile)
     let toCompile = data["compile"]
@@ -869,32 +878,32 @@ proc runJsonBuildInstructions*(projectfile: string) =
 
     let prettyCb = proc (idx: int) =
       echo prettyCmds[idx]
-    execCmdsInParallel(cmds, prettyCb)
+    execCmdsInParallel(conf, cmds, prettyCb)
 
     let linkCmd = data["linkcmd"]
     doAssert linkCmd.kind == JString
-    execLinkCmd(linkCmd.getStr)
+    execLinkCmd(conf, linkCmd.getStr)
   except:
     echo getCurrentException().getStackTrace()
     quit "error evaluating JSON file: " & jsonFile
 
-proc genMappingFiles(list: CFileList): Rope =
+proc genMappingFiles(conf: ConfigRef; list: CFileList): Rope =
   for it in list:
     addf(result, "--file:r\"$1\"$N", [rope(it.cname)])
 
-proc writeMapping*(gSymbolMapping: Rope) =
-  if optGenMapping notin gGlobalOptions: return
+proc writeMapping*(conf: ConfigRef; symbolMapping: Rope) =
+  if optGenMapping notin conf.globalOptions: return
   var code = rope("[C_Files]\n")
-  add(code, genMappingFiles(toCompile))
+  add(code, genMappingFiles(conf, toCompile))
   add(code, "\n[C_Compiler]\nFlags=")
-  add(code, strutils.escape(getCompileOptions()))
+  add(code, strutils.escape(getCompileOptions(conf)))
 
   add(code, "\n[Linker]\nFlags=")
-  add(code, strutils.escape(getLinkOptions() & " " &
-                            getConfigVar(cCompiler, ".options.linker")))
+  add(code, strutils.escape(getLinkOptions(conf) & " " &
+                            getConfigVar(conf, cCompiler, ".options.linker")))
 
   add(code, "\n[Environment]\nlibpath=")
-  add(code, strutils.escape(libpath))
+  add(code, strutils.escape(conf.libpath))
 
-  addf(code, "\n[Symbols]$n$1", [gSymbolMapping])
-  writeRope(code, joinPath(gProjectPath, "mapping.txt"))
+  addf(code, "\n[Symbols]$n$1", [symbolMapping])
+  writeRope(code, joinPath(conf.projectPath, "mapping.txt"))
diff --git a/compiler/filter_tmpl.nim b/compiler/filter_tmpl.nim
index ca9a3a801..f93355e6b 100644
--- a/compiler/filter_tmpl.nim
+++ b/compiler/filter_tmpl.nim
@@ -27,7 +27,7 @@ type
     emit, conc, toStr: string
     curly, bracket, par: int
     pendingExprLine: bool
-
+    config: ConfigRef
 
 const
   PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF', '.', '_'}
@@ -35,16 +35,16 @@ const
 proc newLine(p: var TTmplParser) =
   llStreamWrite(p.outp, repeat(')', p.emitPar))
   p.emitPar = 0
-  if p.info.line > int16(1): llStreamWrite(p.outp, "\n")
+  if p.info.line > uint16(1): llStreamWrite(p.outp, "\n")
   if p.pendingExprLine:
     llStreamWrite(p.outp, spaces(2))
     p.pendingExprLine = false
 
 proc scanPar(p: var TTmplParser, d: int) =
   var i = d
-  while true:
+  let hi = p.x.len - 1
+  while i <= hi:
     case p.x[i]
-    of '\0': break
     of '(': inc(p.par)
     of ')': dec(p.par)
     of '[': inc(p.bracket)
@@ -63,16 +63,22 @@ const
 
 proc parseLine(p: var TTmplParser) =
   var j = 0
-  while p.x[j] == ' ': inc(j)
-  if p.x[0] == p.nimDirective and p.x[1] == '?':
+  let hi = p.x.len - 1
+
+  if hi == 0:
+    return
+
+  while j <= hi and p.x[j] == ' ': inc(j)
+
+  if p.x.len >= 2 and p.x[0] == p.nimDirective and p.x[1] == '?':
     newLine(p)
-  elif p.x[j] == p.nimDirective:
+  elif j < p.x.len and p.x[j] == p.nimDirective:
     newLine(p)
     inc(j)
-    while p.x[j] == ' ': inc(j)
+    while j <= hi and p.x[j] == ' ': inc(j)
     let d = j
     var keyw = ""
-    while p.x[j] in PatternChars:
+    while j <= hi and p.x[j] in PatternChars:
       add(keyw, p.x[j])
       inc(j)
 
@@ -84,7 +90,7 @@ proc parseLine(p: var TTmplParser) =
         dec(p.indent, 2)
       else:
         p.info.col = int16(j)
-        localError(p.info, errXNotAllowedHere, "end")
+        localError(p.config, p.info, "'end' does not close a control flow construct")
       llStreamWrite(p.outp, spaces(p.indent))
       llStreamWrite(p.outp, "#end")
     of "if", "when", "try", "while", "for", "block", "case", "proc", "iterator",
@@ -126,10 +132,8 @@ proc parseLine(p: var TTmplParser) =
       llStreamWrite(p.outp, "(\"")
       inc(p.emitPar)
     p.state = psTempl
-    while true:
+    while j <= hi:
       case p.x[j]
-      of '\0':
-        break
       of '\x01'..'\x1F', '\x80'..'\xFF':
         llStreamWrite(p.outp, "\\x")
         llStreamWrite(p.outp, toHex(ord(p.x[j]), 2))
@@ -156,11 +160,8 @@ proc parseLine(p: var TTmplParser) =
             llStreamWrite(p.outp, '(')
             inc(j)
             var curly = 0
-            while true:
+            while j <= hi:
               case p.x[j]
-              of '\0':
-                localError(p.info, errXExpected, "}")
-                break
               of '{':
                 inc(j)
                 inc(curly)
@@ -173,6 +174,9 @@ proc parseLine(p: var TTmplParser) =
               else:
                 llStreamWrite(p.outp, p.x[j])
                 inc(j)
+            if curly > 0:
+              localError(p.config, p.info, "expected closing '}'")
+              break
             llStreamWrite(p.outp, ')')
             llStreamWrite(p.outp, p.conc)
             llStreamWrite(p.outp, '\"')
@@ -181,7 +185,7 @@ proc parseLine(p: var TTmplParser) =
             llStreamWrite(p.outp, p.conc)
             llStreamWrite(p.outp, p.toStr)
             llStreamWrite(p.outp, '(')
-            while p.x[j] in PatternChars:
+            while j <= hi and p.x[j] in PatternChars:
               llStreamWrite(p.outp, p.x[j])
               inc(j)
             llStreamWrite(p.outp, ')')
@@ -193,28 +197,29 @@ proc parseLine(p: var TTmplParser) =
               inc(j)
             else:
               p.info.col = int16(j)
-              localError(p.info, errInvalidExpression, "$")
+              localError(p.config, p.info, "invalid expression")
         else:
           llStreamWrite(p.outp, p.x[j])
           inc(j)
     llStreamWrite(p.outp, "\\n\"")
 
-proc filterTmpl*(stdin: PLLStream, filename: string, call: PNode): PLLStream =
+proc filterTmpl*(stdin: PLLStream, filename: string, call: PNode; conf: ConfigRef): PLLStream =
   var p: TTmplParser
-  p.info = newLineInfo(filename, 0, 0)
+  p.config = conf
+  p.info = newLineInfo(conf, filename, 0, 0)
   p.outp = llStreamOpen("")
   p.inp = stdin
-  p.subsChar = charArg(call, "subschar", 1, '$')
-  p.nimDirective = charArg(call, "metachar", 2, '#')
-  p.emit = strArg(call, "emit", 3, "result.add")
-  p.conc = strArg(call, "conc", 4, " & ")
-  p.toStr = strArg(call, "tostring", 5, "$")
+  p.subsChar = charArg(conf, call, "subschar", 1, '$')
+  p.nimDirective = charArg(conf, call, "metachar", 2, '#')
+  p.emit = strArg(conf, call, "emit", 3, "result.add")
+  p.conc = strArg(conf, call, "conc", 4, " & ")
+  p.toStr = strArg(conf, call, "tostring", 5, "$")
   p.x = newStringOfCap(120)
   # do not process the first line which contains the directive:
   if llStreamReadLine(p.inp, p.x):
-    p.info.line = p.info.line + int16(1)
+    p.info.line = p.info.line + 1'u16
   while llStreamReadLine(p.inp, p.x):
-    p.info.line = p.info.line + int16(1)
+    p.info.line = p.info.line + 1'u16
     parseLine(p)
   newLine(p)
   result = p.outp
diff --git a/compiler/filters.nim b/compiler/filters.nim
index 37df628ed..3ebbad678 100644
--- a/compiler/filters.nim
+++ b/compiler/filters.nim
@@ -13,43 +13,44 @@ import
   llstream, os, wordrecg, idents, strutils, ast, astalgo, msgs, options,
   renderer
 
-proc invalidPragma(n: PNode) =
-  localError(n.info, errXNotAllowedHere, renderTree(n, {renderNoComments}))
+proc invalidPragma(conf: ConfigRef; n: PNode) =
+  localError(conf, n.info,
+      "'$1' not allowed here" % renderTree(n, {renderNoComments}))
 
-proc getArg(n: PNode, name: string, pos: int): PNode =
+proc getArg(conf: ConfigRef; n: PNode, name: string, pos: int): PNode =
   result = nil
   if n.kind in {nkEmpty..nkNilLit}: return
   for i in countup(1, sonsLen(n) - 1):
     if n.sons[i].kind == nkExprEqExpr:
-      if n.sons[i].sons[0].kind != nkIdent: invalidPragma(n)
+      if n.sons[i].sons[0].kind != nkIdent: invalidPragma(conf, n)
       if cmpIgnoreStyle(n.sons[i].sons[0].ident.s, name) == 0:
         return n.sons[i].sons[1]
     elif i == pos:
       return n.sons[i]
 
-proc charArg*(n: PNode, name: string, pos: int, default: char): char =
-  var x = getArg(n, name, pos)
+proc charArg*(conf: ConfigRef; n: PNode, name: string, pos: int, default: char): char =
+  var x = getArg(conf, n, name, pos)
   if x == nil: result = default
   elif x.kind == nkCharLit: result = chr(int(x.intVal))
-  else: invalidPragma(n)
+  else: invalidPragma(conf, n)
 
-proc strArg*(n: PNode, name: string, pos: int, default: string): string =
-  var x = getArg(n, name, pos)
+proc strArg*(conf: ConfigRef; n: PNode, name: string, pos: int, default: string): string =
+  var x = getArg(conf, n, name, pos)
   if x == nil: result = default
   elif x.kind in {nkStrLit..nkTripleStrLit}: result = x.strVal
-  else: invalidPragma(n)
+  else: invalidPragma(conf, n)
 
-proc boolArg*(n: PNode, name: string, pos: int, default: bool): bool =
-  var x = getArg(n, name, pos)
+proc boolArg*(conf: ConfigRef; n: PNode, name: string, pos: int, default: bool): bool =
+  var x = getArg(conf, n, name, pos)
   if x == nil: result = default
   elif x.kind == nkIdent and cmpIgnoreStyle(x.ident.s, "true") == 0: result = true
   elif x.kind == nkIdent and cmpIgnoreStyle(x.ident.s, "false") == 0: result = false
-  else: invalidPragma(n)
+  else: invalidPragma(conf, n)
 
-proc filterStrip*(stdin: PLLStream, filename: string, call: PNode): PLLStream =
-  var pattern = strArg(call, "startswith", 1, "")
-  var leading = boolArg(call, "leading", 2, true)
-  var trailing = boolArg(call, "trailing", 3, true)
+proc filterStrip*(conf: ConfigRef; stdin: PLLStream, filename: string, call: PNode): PLLStream =
+  var pattern = strArg(conf, call, "startswith", 1, "")
+  var leading = boolArg(conf, call, "leading", 2, true)
+  var trailing = boolArg(conf, call, "trailing", 3, true)
   result = llStreamOpen("")
   var line = newStringOfCap(80)
   while llStreamReadLine(stdin, line):
@@ -60,10 +61,10 @@ proc filterStrip*(stdin: PLLStream, filename: string, call: PNode): PLLStream =
       llStreamWriteln(result, line)
   llStreamClose(stdin)
 
-proc filterReplace*(stdin: PLLStream, filename: string, call: PNode): PLLStream =
-  var sub = strArg(call, "sub", 1, "")
-  if len(sub) == 0: invalidPragma(call)
-  var by = strArg(call, "by", 2, "")
+proc filterReplace*(conf: ConfigRef; stdin: PLLStream, filename: string, call: PNode): PLLStream =
+  var sub = strArg(conf, call, "sub", 1, "")
+  if len(sub) == 0: invalidPragma(conf, call)
+  var by = strArg(conf, call, "by", 2, "")
   result = llStreamOpen("")
   var line = newStringOfCap(80)
   while llStreamReadLine(stdin, line):
diff --git a/compiler/gorgeimpl.nim b/compiler/gorgeimpl.nim
index 80302b4b5..44c7651bc 100644
--- a/compiler/gorgeimpl.nim
+++ b/compiler/gorgeimpl.nim
@@ -21,11 +21,11 @@ proc readOutput(p: Process): (string, int) =
     result[0].setLen(result[0].len - "\n".len)
   result[1] = p.waitForExit
 
-proc opGorge*(cmd, input, cache: string, info: TLineInfo): (string, int) =
+proc opGorge*(cmd, input, cache: string, info: TLineInfo; conf: ConfigRef): (string, int) =
   let workingDir = parentDir(info.toFullPath)
   if cache.len > 0:# and optForceFullMake notin gGlobalOptions:
     let h = secureHash(cmd & "\t" & input & "\t" & cache)
-    let filename = options.toGeneratedFile("gorge_" & $h, "txt")
+    let filename = options.toGeneratedFile(conf, "gorge_" & $h, "txt")
     var f: File
     if open(f, filename):
       result = (f.readAll, 0)
diff --git a/compiler/guards.nim b/compiler/guards.nim
index 94af5202d..d39ea799b 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
+  saturate, modulegraphs, options, configuration
 
 const
   someEq = {mEqI, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc,
@@ -83,18 +83,25 @@ proc isLetLocation(m: PNode, isApprox: bool): bool =
 
 proc interestingCaseExpr*(m: PNode): bool = isLetLocation(m, true)
 
-let
-  opLe = createMagic("<=", mLeI)
-  opLt = createMagic("<", mLtI)
-  opAnd = createMagic("and", mAnd)
-  opOr = createMagic("or", mOr)
-  opIsNil = createMagic("isnil", mIsNil)
-  opEq = createMagic("==", mEqI)
-  opAdd = createMagic("+", mAddI)
-  opSub = createMagic("-", mSubI)
-  opMul = createMagic("*", mMulI)
-  opDiv = createMagic("div", mDivI)
-  opLen = createMagic("len", mLengthSeq)
+type
+  Operators* = object
+    opNot, opContains, opLe, opLt, opAnd, opOr, opIsNil, opEq: PSym
+    opAdd, opSub, opMul, opDiv, opLen: PSym
+
+proc initOperators*(g: ModuleGraph): Operators =
+  result.opLe = createMagic(g, "<=", mLeI)
+  result.opLt = createMagic(g, "<", mLtI)
+  result.opAnd = createMagic(g, "and", mAnd)
+  result.opOr = createMagic(g, "or", mOr)
+  result.opIsNil = createMagic(g, "isnil", mIsNil)
+  result.opEq = createMagic(g, "==", mEqI)
+  result.opAdd = createMagic(g, "+", mAddI)
+  result.opSub = createMagic(g, "-", mSubI)
+  result.opMul = createMagic(g, "*", mMulI)
+  result.opDiv = createMagic(g, "div", mDivI)
+  result.opLen = createMagic(g, "len", mLengthSeq)
+  result.opNot = createMagic(g, "not", mNot)
+  result.opContains = createMagic(g, "contains", mInSet)
 
 proc swapArgs(fact: PNode, newOp: PSym): PNode =
   result = newNodeI(nkCall, fact.info, 3)
@@ -102,16 +109,16 @@ proc swapArgs(fact: PNode, newOp: PSym): PNode =
   result.sons[1] = fact.sons[2]
   result.sons[2] = fact.sons[1]
 
-proc neg(n: PNode): PNode =
+proc neg(n: PNode; o: Operators): PNode =
   if n == nil: return nil
   case n.getMagic
   of mNot:
     result = n.sons[1]
   of someLt:
     # not (a < b)  ==  a >= b  ==  b <= a
-    result = swapArgs(n, opLe)
+    result = swapArgs(n, o.opLe)
   of someLe:
-    result = swapArgs(n, opLt)
+    result = swapArgs(n, o.opLt)
   of mInSet:
     if n.sons[1].kind != nkCurly: return nil
     let t = n.sons[2].typ.skipTypes(abstractInst)
@@ -133,11 +140,11 @@ proc neg(n: PNode): PNode =
   of mOr:
     # not (a or b) --> not a and not b
     let
-      a = n.sons[1].neg
-      b = n.sons[2].neg
+      a = n.sons[1].neg(o)
+      b = n.sons[2].neg(o)
     if a != nil and b != nil:
       result = newNodeI(nkCall, n.info, 3)
-      result.sons[0] = newSymNode(opAnd)
+      result.sons[0] = newSymNode(o.opAnd)
       result.sons[1] = a
       result.sons[2] = b
     elif a != nil:
@@ -147,7 +154,7 @@ proc neg(n: PNode): PNode =
   else:
     # leave  not (a == 4)  as it is
     result = newNodeI(nkCall, n.info, 2)
-    result.sons[0] = newSymNode(opNot)
+    result.sons[0] = newSymNode(o.opNot)
     result.sons[1] = n
 
 proc buildCall(op: PSym; a: PNode): PNode =
@@ -181,7 +188,7 @@ proc `|div|`(a, b: PNode): PNode =
   if a.kind in {nkCharLit..nkUInt64Lit}: result.intVal = a.intVal div b.intVal
   else: result.floatVal = a.floatVal / b.floatVal
 
-proc negate(a, b, res: PNode): PNode =
+proc negate(a, b, res: PNode; o: Operators): PNode =
   if b.kind in {nkCharLit..nkUInt64Lit} and b.intVal != low(BiggestInt):
     var b = copyNode(b)
     b.intVal = -b.intVal
@@ -189,11 +196,11 @@ proc negate(a, b, res: PNode): PNode =
       b.intVal = b.intVal |+| a.intVal
       result = b
     else:
-      result = buildCall(opAdd, a, b)
+      result = buildCall(o.opAdd, a, b)
   elif b.kind in {nkFloatLit..nkFloat64Lit}:
     var b = copyNode(b)
     b.floatVal = -b.floatVal
-    result = buildCall(opAdd, a, b)
+    result = buildCall(o.opAdd, a, b)
   else:
     result = res
 
@@ -205,7 +212,7 @@ proc lowBound*(x: PNode): PNode =
   result = nkIntLit.newIntNode(firstOrd(x.typ))
   result.info = x.info
 
-proc highBound*(x: PNode): PNode =
+proc highBound*(x: PNode; o: Operators): PNode =
   let typ = x.typ.skipTypes(abstractInst)
   result = if typ.kind == tyArray:
              nkIntLit.newIntNode(lastOrd(typ))
@@ -213,23 +220,23 @@ proc highBound*(x: PNode): PNode =
                x.sym.kind == skConst:
              nkIntLit.newIntNode(x.sym.ast.len-1)
            else:
-             opAdd.buildCall(opLen.buildCall(x), minusOne())
+             o.opAdd.buildCall(o.opLen.buildCall(x), minusOne())
   result.info = x.info
 
-proc reassociation(n: PNode): PNode =
+proc reassociation(n: PNode; o: Operators): PNode =
   result = n
   # (foo+5)+5 --> foo+10;  same for '*'
   case result.getMagic
   of someAdd:
     if result[2].isValue and
         result[1].getMagic in someAdd and result[1][2].isValue:
-      result = opAdd.buildCall(result[1][1], result[1][2] |+| result[2])
+      result = o.opAdd.buildCall(result[1][1], result[1][2] |+| result[2])
       if result[2].intVal == 0:
         result = result[1]
   of someMul:
     if result[2].isValue and
         result[1].getMagic in someMul and result[1][2].isValue:
-      result = opMul.buildCall(result[1][1], result[1][2] |*| result[2])
+      result = o.opMul.buildCall(result[1][1], result[1][2] |*| result[2])
       if result[2].intVal == 1:
         result = result[1]
       elif result[2].intVal == 0:
@@ -243,12 +250,12 @@ proc pred(n: PNode): PNode =
   else:
     result = n
 
-proc canon*(n: PNode): PNode =
+proc canon*(n: PNode; o: Operators): PNode =
   # XXX for now only the new code in 'semparallel' uses this
   if n.safeLen >= 1:
     result = shallowCopy(n)
     for i in 0 ..< n.len:
-      result.sons[i] = canon(n.sons[i])
+      result.sons[i] = canon(n.sons[i], o)
   elif n.kind == nkSym and n.sym.kind == skLet and
       n.sym.ast.getMagic in (someEq + someAdd + someMul + someMin +
       someMax + someHigh + {mUnaryLt} + someSub + someLen + someDiv):
@@ -263,24 +270,24 @@ proc canon*(n: PNode): PNode =
       # (4 + foo) + 2 --> (foo + 4) + 2
   of someHigh:
     # high == len+(-1)
-    result = opAdd.buildCall(opLen.buildCall(result[1]), minusOne())
+    result = o.opAdd.buildCall(o.opLen.buildCall(result[1]), minusOne())
   of mUnaryLt:
-    result = buildCall(opAdd, result[1], minusOne())
+    result = buildCall(o.opAdd, result[1], minusOne())
   of someSub:
     # x - 4  -->  x + (-4)
-    result = negate(result[1], result[2], result)
+    result = negate(result[1], result[2], result, o)
   of someLen:
-    result.sons[0] = opLen.newSymNode
+    result.sons[0] = o.opLen.newSymNode
   of someLt:
     # x < y  same as x <= y-1:
-    let y = n[2].canon
+    let y = n[2].canon(o)
     let p = pred(y)
-    let minus = if p != y: p else: opAdd.buildCall(y, minusOne()).canon
-    result = opLe.buildCall(n[1].canon, minus)
+    let minus = if p != y: p else: o.opAdd.buildCall(y, minusOne()).canon(o)
+    result = o.opLe.buildCall(n[1].canon(o), minus)
   else: discard
 
   result = skipConv(result)
-  result = reassociation(result)
+  result = reassociation(result, o)
   # most important rule: (x-4) <= a.len -->  x <= a.len+4
   case result.getMagic
   of someLe:
@@ -291,10 +298,10 @@ proc canon*(n: PNode): PNode =
       case x.getMagic
       of someSub:
         result = buildCall(result[0].sym, x[1],
-                           reassociation(opAdd.buildCall(y, x[2])))
+                           reassociation(o.opAdd.buildCall(y, x[2]), o))
       of someAdd:
         # Rule A:
-        let plus = negate(y, x[2], nil).reassociation
+        let plus = negate(y, x[2], nil, o).reassociation(o)
         if plus != nil: result = buildCall(result[0].sym, x[1], plus)
       else: discard
     elif y.kind in nkCallKinds and y.len == 3 and y[2].isValue and
@@ -303,9 +310,9 @@ proc canon*(n: PNode): PNode =
       case y.getMagic
       of someSub:
         result = buildCall(result[0].sym, y[1],
-                           reassociation(opAdd.buildCall(x, y[2])))
+                           reassociation(o.opAdd.buildCall(x, y[2]), o))
       of someAdd:
-        let plus = negate(x, y[2], nil).reassociation
+        let plus = negate(x, y[2], nil, o).reassociation(o)
         # ensure that Rule A will not trigger afterwards with the
         # additional 'not isLetLocation' constraint:
         if plus != nil and not isLetLocation(x, true):
@@ -323,15 +330,15 @@ proc canon*(n: PNode): PNode =
       result.sons[2] = y[1]
   else: discard
 
-proc `+@`*(a: PNode; b: BiggestInt): PNode =
-  canon(if b != 0: opAdd.buildCall(a, nkIntLit.newIntNode(b)) else: a)
+proc buildAdd*(a: PNode; b: BiggestInt; o: Operators): PNode =
+  canon(if b != 0: o.opAdd.buildCall(a, nkIntLit.newIntNode(b)) else: a, o)
 
-proc usefulFact(n: PNode): PNode =
+proc usefulFact(n: PNode; o: Operators): PNode =
   case n.getMagic
   of someEq:
     if skipConv(n.sons[2]).kind == nkNilLit and (
         isLetLocation(n.sons[1], false) or isVar(n.sons[1])):
-      result = opIsNil.buildCall(n.sons[1])
+      result = o.opIsNil.buildCall(n.sons[1])
     else:
       if isLetLocation(n.sons[1], true) or isLetLocation(n.sons[2], true):
         # XXX algebraic simplifications!  'i-1 < a.len' --> 'i < a.len+1'
@@ -351,11 +358,11 @@ proc usefulFact(n: PNode): PNode =
       result = n
   of mAnd:
     let
-      a = usefulFact(n.sons[1])
-      b = usefulFact(n.sons[2])
+      a = usefulFact(n.sons[1], o)
+      b = usefulFact(n.sons[2], o)
     if a != nil and b != nil:
       result = newNodeI(nkCall, n.info, 3)
-      result.sons[0] = newSymNode(opAnd)
+      result.sons[0] = newSymNode(o.opAnd)
       result.sons[1] = a
       result.sons[2] = b
     elif a != nil:
@@ -363,9 +370,9 @@ proc usefulFact(n: PNode): PNode =
     elif b != nil:
       result = b
   of mNot:
-    let a = usefulFact(n.sons[1])
+    let a = usefulFact(n.sons[1], o)
     if a != nil:
-      result = a.neg
+      result = a.neg(o)
   of mOr:
     # 'or' sucks! (p.isNil or q.isNil) --> hard to do anything
     # with that knowledge...
@@ -374,14 +381,14 @@ proc usefulFact(n: PNode): PNode =
     #  (x == 3) or (y == 2)  ---> not ( not (x==3) and not (y == 2))
     #  not (x != 3 and y != 2)
     let
-      a = usefulFact(n.sons[1]).neg
-      b = usefulFact(n.sons[2]).neg
+      a = usefulFact(n.sons[1], o).neg(o)
+      b = usefulFact(n.sons[2], o).neg(o)
     if a != nil and b != nil:
       result = newNodeI(nkCall, n.info, 3)
-      result.sons[0] = newSymNode(opAnd)
+      result.sons[0] = newSymNode(o.opAnd)
       result.sons[1] = a
       result.sons[2] = b
-      result = result.neg
+      result = result.neg(o)
   elif n.kind == nkSym and n.sym.kind == skLet:
     # consider:
     #   let a = 2 < x
@@ -389,32 +396,34 @@ proc usefulFact(n: PNode): PNode =
     #     ...
     # We make can easily replace 'a' by '2 < x' here:
     if n.sym.ast != nil:
-      result = usefulFact(n.sym.ast)
+      result = usefulFact(n.sym.ast, o)
   elif n.kind == nkStmtListExpr:
-    result = usefulFact(n.lastSon)
+    result = usefulFact(n.lastSon, o)
 
 type
-  TModel* = seq[PNode] # the "knowledge base"
+  TModel* = object
+    s*: seq[PNode] # the "knowledge base"
+    o*: Operators
 
 proc addFact*(m: var TModel, nn: PNode) =
-  let n = usefulFact(nn)
-  if n != nil: m.add n
+  let n = usefulFact(nn, m.o)
+  if n != nil: m.s.add n
 
 proc addFactNeg*(m: var TModel, n: PNode) =
-  let n = n.neg
+  let n = n.neg(m.o)
   if n != nil: addFact(m, n)
 
-proc canonOpr(opr: PSym): PSym =
-  case opr.magic
-  of someEq: result = opEq
-  of someLe: result = opLe
-  of someLt: result = opLt
-  of someLen: result = opLen
-  of someAdd: result = opAdd
-  of someSub: result = opSub
-  of someMul: result = opMul
-  of someDiv: result = opDiv
-  else: result = opr
+proc sameOpr(a, b: PSym): bool =
+  case a.magic
+  of someEq: result = b.magic in someEq
+  of someLe: result = b.magic in someLe
+  of someLt: result = b.magic in someLt
+  of someLen: result = b.magic in someLen
+  of someAdd: result = b.magic in someAdd
+  of someSub: result = b.magic in someSub
+  of someMul: result = b.magic in someMul
+  of someDiv: result = b.magic in someDiv
+  else: result = a == b
 
 proc sameTree*(a, b: PNode): bool =
   result = false
@@ -425,7 +434,7 @@ proc sameTree*(a, b: PNode): bool =
     of nkSym:
       result = a.sym == b.sym
       if not result and a.sym.magic != mNone:
-        result = a.sym.magic == b.sym.magic or canonOpr(a.sym) == canonOpr(b.sym)
+        result = a.sym.magic == b.sym.magic or sameOpr(a.sym, b.sym)
     of nkIdent: result = a.ident.id == b.ident.id
     of nkCharLit..nkInt64Lit: result = a.intVal == b.intVal
     of nkFloatLit..nkFloat64Lit: result = a.floatVal == b.floatVal
@@ -462,8 +471,8 @@ proc invalidateFacts*(m: var TModel, n: PNode) =
   # The same mechanism could be used for more complex data stored on the heap;
   # procs that 'write: []' cannot invalidate 'n.kind' for instance. In fact, we
   # could CSE these expressions then and help C's optimizer.
-  for i in 0..high(m):
-    if m[i] != nil and m[i].hasSubTree(n): m[i] = nil
+  for i in 0..high(m.s):
+    if m.s[i] != nil and m.s[i].hasSubTree(n): m.s[i] = nil
 
 proc valuesUnequal(a, b: PNode): bool =
   if a.isValue and b.isValue:
@@ -486,7 +495,7 @@ proc impliesEq(fact, eq: PNode): TImplication =
     if sameTree(fact.sons[2], eq.sons[loc]) and isValue(eq.sons[val]):
       if inSet(fact.sons[1], eq.sons[val]): result = impYes
       else: result = impNo
-  of mNot, mOr, mAnd: internalError(eq.info, "impliesEq")
+  of mNot, mOr, mAnd: assert(false, "impliesEq")
   else: discard
 
 proc leImpliesIn(x, c, aSet: PNode): TImplication =
@@ -549,7 +558,7 @@ proc impliesIn(fact, loc, aSet: PNode): TImplication =
     elif sameTree(fact.sons[2], loc):
       # 4 < x  -->  3 <= x
       result = geImpliesIn(fact.sons[2], fact.sons[1].pred, aSet)
-  of mNot, mOr, mAnd: internalError(loc.info, "impliesIn")
+  of mNot, mOr, mAnd: assert(false, "impliesIn")
   else: discard
 
 proc valueIsNil(n: PNode): TImplication =
@@ -567,11 +576,11 @@ proc impliesIsNil(fact, eq: PNode): TImplication =
       result = valueIsNil(fact.sons[2].skipConv)
     elif sameTree(fact.sons[2], eq.sons[1]):
       result = valueIsNil(fact.sons[1].skipConv)
-  of mNot, mOr, mAnd: internalError(eq.info, "impliesIsNil")
+  of mNot, mOr, mAnd: assert(false, "impliesIsNil")
   else: discard
 
 proc impliesGe(fact, x, c: PNode): TImplication =
-  internalAssert isLocation(x)
+  assert isLocation(x)
   case fact.sons[0].sym.magic
   of someEq:
     if sameTree(fact.sons[1], x):
@@ -603,7 +612,7 @@ proc impliesGe(fact, x, c: PNode): TImplication =
       # fact: 3 <= x; question: x >= 2 ?  --> true iff 2 <= 3
       if isValue(fact.sons[1]) and isValue(c):
         if leValue(c, fact.sons[1]): result = impYes
-  of mNot, mOr, mAnd: internalError(x.info, "impliesGe")
+  of mNot, mOr, mAnd: assert(false, "impliesGe")
   else: discard
 
 proc impliesLe(fact, x, c: PNode): TImplication =
@@ -643,7 +652,7 @@ proc impliesLe(fact, x, c: PNode): TImplication =
       if isValue(fact.sons[1]) and isValue(c):
         if leValue(c, fact.sons[1].pred): result = impNo
 
-  of mNot, mOr, mAnd: internalError(x.info, "impliesLe")
+  of mNot, mOr, mAnd: assert(false, "impliesLe")
   else: discard
 
 proc impliesLt(fact, x, c: PNode): TImplication =
@@ -707,14 +716,14 @@ proc factImplies(fact, prop: PNode): TImplication =
 
 proc doesImply*(facts: TModel, prop: PNode): TImplication =
   assert prop.kind in nkCallKinds
-  for f in facts:
+  for f in facts.s:
     # facts can be invalidated, in which case they are 'nil':
     if not f.isNil:
       result = f.factImplies(prop)
       if result != impUnknown: return
 
-proc impliesNotNil*(facts: TModel, arg: PNode): TImplication =
-  result = doesImply(facts, opIsNil.buildCall(arg).neg)
+proc impliesNotNil*(m: TModel, arg: PNode): TImplication =
+  result = doesImply(m, m.o.opIsNil.buildCall(arg).neg(m.o))
 
 proc simpleSlice*(a, b: PNode): BiggestInt =
   # returns 'c' if a..b matches (i+c)..(i+c), -1 otherwise. (i)..(i) is matched
@@ -817,20 +826,20 @@ proc ple(m: TModel; a, b: PNode): TImplication =
   if a.getMagic in someMul and a[2].isValue and a[1].getMagic in someDiv and
       a[1][2].isValue:
     # simplify   (x div 4) * 2 <= y   to  x div (c div d)  <= y
-    if ple(m, buildCall(opDiv, a[1][1], `|div|`(a[1][2], a[2])), b) == impYes:
+    if ple(m, buildCall(m.o.opDiv, a[1][1], `|div|`(a[1][2], a[2])), b) == impYes:
       return impYes
 
   # x*3 + x == x*4. It follows that:
   # x*3 + y <= x*4  if  y <= x  and 3 <= 4
   if a =~ x*dc + y and b =~ x2*ec:
     if sameTree(x, x2):
-      let ec1 = opAdd.buildCall(ec, minusOne())
+      let ec1 = m.o.opAdd.buildCall(ec, minusOne())
       if x >=? 1 and ec >=? 1 and dc >=? 1 and dc <=? ec1 and y <=? x:
         return impYes
   elif a =~ x*dc and b =~ x2*ec + y:
     #echo "BUG cam ehrer e ", a, " <=? ", b
     if sameTree(x, x2):
-      let ec1 = opAdd.buildCall(ec, minusOne())
+      let ec1 = m.o.opAdd.buildCall(ec, minusOne())
       if x >=? 1 and ec >=? 1 and dc >=? 1 and dc <=? ec1 and y <=? zero():
         return impYes
 
@@ -863,9 +872,9 @@ proc ple(m: TModel; a, b: PNode): TImplication =
 
   # use the knowledge base:
   return pleViaModel(m, a, b)
-  #return doesImply(m, opLe.buildCall(a, b))
+  #return doesImply(m, o.opLe.buildCall(a, b))
 
-type TReplacements = seq[tuple[a,b: PNode]]
+type TReplacements = seq[tuple[a, b: PNode]]
 
 proc replaceSubTree(n, x, by: PNode): PNode =
   if sameTree(n, x):
@@ -883,11 +892,11 @@ proc applyReplacements(n: PNode; rep: TReplacements): PNode =
 
 proc pleViaModelRec(m: var TModel; a, b: PNode): TImplication =
   # now check for inferrable facts: a <= b and b <= c  implies a <= c
-  for i in 0..m.high:
-    let fact = m[i]
+  for i in 0..m.s.high:
+    let fact = m.s[i]
     if fact != nil and fact.getMagic in someLe:
       # mark as used:
-      m[i] = nil
+      m.s[i] = nil
       # i <= len-100
       # i <=? len-1
       # --> true  if  (len-100) <= (len-1)
@@ -919,7 +928,7 @@ proc pleViaModelRec(m: var TModel; a, b: PNode): TImplication =
 proc pleViaModel(model: TModel; aa, bb: PNode): TImplication =
   # compute replacements:
   var replacements: TReplacements = @[]
-  for fact in model:
+  for fact in model.s:
     if fact != nil and fact.getMagic in someEq:
       let a = fact[1]
       let b = fact[2]
@@ -929,12 +938,13 @@ proc pleViaModel(model: TModel; aa, bb: PNode): TImplication =
   var a = aa
   var b = bb
   if replacements.len > 0:
-    m = @[]
+    m.s = @[]
+    m.o = model.o
     # make the other facts consistent:
-    for fact in model:
+    for fact in model.s:
       if fact != nil and fact.getMagic notin someEq:
         # XXX 'canon' should not be necessary here, but it is
-        m.add applyReplacements(fact, replacements).canon
+        m.s.add applyReplacements(fact, replacements).canon(m.o)
     a = applyReplacements(aa, replacements)
     b = applyReplacements(bb, replacements)
   else:
@@ -943,31 +953,31 @@ proc pleViaModel(model: TModel; aa, bb: PNode): TImplication =
   result = pleViaModelRec(m, a, b)
 
 proc proveLe*(m: TModel; a, b: PNode): TImplication =
-  let x = canon(opLe.buildCall(a, b))
+  let x = canon(m.o.opLe.buildCall(a, b), m.o)
   #echo "ROOT ", renderTree(x[1]), " <=? ", renderTree(x[2])
   result = ple(m, x[1], x[2])
   if result == impUnknown:
     # try an alternative:  a <= b  iff  not (b < a)  iff  not (b+1 <= a):
-    let y = canon(opLe.buildCall(opAdd.buildCall(b, one()), a))
+    let y = canon(m.o.opLe.buildCall(m.o.opAdd.buildCall(b, one()), a), m.o)
     result = ~ple(m, y[1], y[2])
 
 proc addFactLe*(m: var TModel; a, b: PNode) =
-  m.add canon(opLe.buildCall(a, b))
+  m.s.add canon(m.o.opLe.buildCall(a, b), m.o)
 
 proc settype(n: PNode): PType =
   result = newType(tySet, n.typ.owner)
   addSonSkipIntLit(result, n.typ)
 
-proc buildOf(it, loc: PNode): PNode =
+proc buildOf(it, loc: PNode; o: Operators): PNode =
   var s = newNodeI(nkCurly, it.info, it.len-1)
   s.typ = settype(loc)
   for i in 0..it.len-2: s.sons[i] = it.sons[i]
   result = newNodeI(nkCall, it.info, 3)
-  result.sons[0] = newSymNode(opContains)
+  result.sons[0] = newSymNode(o.opContains)
   result.sons[1] = s
   result.sons[2] = loc
 
-proc buildElse(n: PNode): PNode =
+proc buildElse(n: PNode; o: Operators): PNode =
   var s = newNodeIT(nkCurly, n.info, settype(n.sons[0]))
   for i in 1..n.len-2:
     let branch = n.sons[i]
@@ -975,23 +985,23 @@ proc buildElse(n: PNode): PNode =
     for j in 0..branch.len-2:
       s.add(branch.sons[j])
   result = newNodeI(nkCall, n.info, 3)
-  result.sons[0] = newSymNode(opContains)
+  result.sons[0] = newSymNode(o.opContains)
   result.sons[1] = s
   result.sons[2] = n.sons[0]
 
 proc addDiscriminantFact*(m: var TModel, n: PNode) =
   var fact = newNodeI(nkCall, n.info, 3)
-  fact.sons[0] = newSymNode(opEq)
+  fact.sons[0] = newSymNode(m.o.opEq)
   fact.sons[1] = n.sons[0]
   fact.sons[2] = n.sons[1]
-  m.add fact
+  m.s.add fact
 
 proc addAsgnFact*(m: var TModel, key, value: PNode) =
   var fact = newNodeI(nkCall, key.info, 3)
-  fact.sons[0] = newSymNode(opEq)
+  fact.sons[0] = newSymNode(m.o.opEq)
   fact.sons[1] = key
   fact.sons[2] = value
-  m.add fact
+  m.s.add fact
 
 proc sameSubexprs*(m: TModel; a, b: PNode): bool =
   # This should be used to check whether two *path expressions* refer to the
@@ -1004,7 +1014,7 @@ proc sameSubexprs*(m: TModel; a, b: PNode): bool =
   # However, nil checking requires exactly the same mechanism! But for now
   # we simply use sameTree and live with the unsoundness of the analysis.
   var check = newNodeI(nkCall, a.info, 3)
-  check.sons[0] = newSymNode(opEq)
+  check.sons[0] = newSymNode(m.o.opEq)
   check.sons[1] = a
   check.sons[2] = b
   result = m.doesImply(check) == impYes
@@ -1012,11 +1022,11 @@ proc sameSubexprs*(m: TModel; a, b: PNode): bool =
 proc addCaseBranchFacts*(m: var TModel, n: PNode, i: int) =
   let branch = n.sons[i]
   if branch.kind == nkOfBranch:
-    m.add buildOf(branch, n.sons[0])
+    m.s.add buildOf(branch, n.sons[0], m.o)
   else:
-    m.add n.buildElse.neg
+    m.s.add n.buildElse(m.o).neg(m.o)
 
-proc buildProperFieldCheck(access, check: PNode): PNode =
+proc buildProperFieldCheck(access, check: PNode; o: Operators): PNode =
   if check.sons[1].kind == nkCurly:
     result = copyTree(check)
     if access.kind == nkDotExpr:
@@ -1028,10 +1038,10 @@ proc buildProperFieldCheck(access, check: PNode): PNode =
   else:
     # it is some 'not'
     assert check.getMagic == mNot
-    result = buildProperFieldCheck(access, check.sons[1]).neg
+    result = buildProperFieldCheck(access, check.sons[1], o).neg(o)
 
-proc checkFieldAccess*(m: TModel, n: PNode) =
+proc checkFieldAccess*(m: TModel, n: PNode; conf: ConfigRef) =
   for i in 1..n.len-1:
-    let check = buildProperFieldCheck(n.sons[0], n.sons[i])
+    let check = buildProperFieldCheck(n.sons[0], n.sons[i], m.o)
     if check != nil and m.doesImply(check) != impYes:
-      message(n.info, warnProveField, renderTree(n.sons[0])); break
+      message(conf, n.info, warnProveField, renderTree(n.sons[0])); break
diff --git a/compiler/hlo.nim b/compiler/hlo.nim
index 2bffaa173..8251e3179 100644
--- a/compiler/hlo.nim
+++ b/compiler/hlo.nim
@@ -12,12 +12,12 @@
 proc hlo(c: PContext, n: PNode): PNode
 
 proc evalPattern(c: PContext, n, orig: PNode): PNode =
-  internalAssert n.kind == nkCall and n.sons[0].kind == nkSym
+  internalAssert c.config, n.kind == nkCall and n.sons[0].kind == nkSym
   # we need to ensure that the resulting AST is semchecked. However, it's
   # aweful to semcheck before macro invocation, so we don't and treat
   # templates and macros as immediate in this context.
   var rule: string
-  if optHints in gOptions and hintPattern in gNotes:
+  if optHints in c.config.options and hintPattern in c.config.notes:
     rule = renderTree(n, {renderNoComments})
   let s = n.sons[0].sym
   case s.kind
@@ -27,8 +27,8 @@ proc evalPattern(c: PContext, n, orig: PNode): PNode =
     result = semTemplateExpr(c, n, s, {efFromHlo})
   else:
     result = semDirectOp(c, n, {})
-  if optHints in gOptions and hintPattern in gNotes:
-    message(orig.info, hintPattern, rule & " --> '" &
+  if optHints in c.config.options and hintPattern in c.config.notes:
+    message(c.config, orig.info, hintPattern, rule & " --> '" &
       renderTree(result, {renderNoComments}) & "'")
 
 proc applyPatterns(c: PContext, n: PNode): PNode =
@@ -44,8 +44,8 @@ proc applyPatterns(c: PContext, n: PNode): PNode =
         assert x.kind in {nkStmtList, nkCall}
         # better be safe than sorry, so check evalTemplateCounter too:
         inc(evalTemplateCounter)
-        if evalTemplateCounter > 100:
-          globalError(n.info, errTemplateInstantiationTooNested)
+        if evalTemplateCounter > evalTemplateLimit:
+          globalError(c.config, n.info, "template instantiation too nested")
         # deactivate this pattern:
         c.patterns[i] = nil
         if x.kind == nkStmtList:
@@ -86,18 +86,18 @@ proc hlo(c: PContext, n: PNode): PNode =
       else:
         result = fitNode(c, n.typ, result, n.info)
       # optimization has been applied so check again:
-      result = commonOptimizations(c.module, result)
+      result = commonOptimizations(c.graph, c.module, result)
       result = hlo(c, result)
-      result = commonOptimizations(c.module, result)
+      result = commonOptimizations(c.graph, c.module, result)
 
 proc hloBody(c: PContext, n: PNode): PNode =
   # fast exit:
-  if c.patterns.len == 0 or optPatterns notin gOptions: return n
+  if c.patterns.len == 0 or optPatterns notin c.config.options: return n
   c.hloLoopDetector = 0
   result = hlo(c, n)
 
 proc hloStmt(c: PContext, n: PNode): PNode =
   # fast exit:
-  if c.patterns.len == 0 or optPatterns notin gOptions: return n
+  if c.patterns.len == 0 or optPatterns notin c.config.options: return n
   c.hloLoopDetector = 0
   result = hlo(c, n)
diff --git a/compiler/idgen.nim b/compiler/idgen.nim
index c6b1a4d07..7d103ffd7 100644
--- a/compiler/idgen.nim
+++ b/compiler/idgen.nim
@@ -36,20 +36,20 @@ proc setId*(id: int) {.inline.} =
 proc idSynchronizationPoint*(idRange: int) =
   gFrontEndId = (gFrontEndId div idRange + 1) * idRange + 1
 
-proc toGid(f: string): string =
+proc toGid(conf: ConfigRef; f: string): string =
   # we used to use ``f.addFileExt("gid")`` (aka ``$project.gid``), but this
   # will cause strange bugs if multiple projects are in the same folder, so
   # we simply use a project independent name:
-  result = options.completeGeneratedFilePath("nim.gid")
+  result = options.completeGeneratedFilePath(conf, "nim.gid")
 
-proc saveMaxIds*(project: string) =
-  var f = open(project.toGid, fmWrite)
+proc saveMaxIds*(conf: ConfigRef; project: string) =
+  var f = open(toGid(conf, project), fmWrite)
   f.writeLine($gFrontEndId)
   f.close()
 
-proc loadMaxIds*(project: string) =
+proc loadMaxIds*(conf: ConfigRef; project: string) =
   var f: File
-  if open(f, project.toGid, fmRead):
+  if open(f, toGid(conf, project), fmRead):
     var line = newStringOfCap(20)
     if f.readLine(line):
       var frontEndId = parseInt(line)
diff --git a/compiler/importer.nim b/compiler/importer.nim
index f4903e6c4..90e774a50 100644
--- a/compiler/importer.nim
+++ b/compiler/importer.nim
@@ -11,11 +11,18 @@
 
 import
   intsets, strutils, os, ast, astalgo, msgs, options, idents, rodread, lookups,
-  semdata, passes, renderer, modulepaths, sigmatch
+  semdata, passes, renderer, modulepaths, sigmatch, configuration
 
 proc evalImport*(c: PContext, n: PNode): PNode
 proc evalFrom*(c: PContext, n: PNode): PNode
 
+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])
+    result.incl(ident.id)
+
 proc importPureEnumField*(c: PContext; s: PSym) =
   var check = strTableGet(c.importTable.symbols, s.name)
   if check == nil:
@@ -40,7 +47,7 @@ proc rawImportSymbol(c: PContext, s: PSym) =
       for j in countup(0, sonsLen(etyp.n) - 1):
         var e = etyp.n.sons[j].sym
         if e.kind != skEnumField:
-          internalError(s.info, "rawImportSymbol")
+          internalError(c.config, s.info, "rawImportSymbol")
           # BUGFIX: because of aliases for enums the symbol may already
           # have been put into the symbol table
           # BUGFIX: but only iff they are the same symbols!
@@ -62,14 +69,14 @@ proc rawImportSymbol(c: PContext, s: PSym) =
     if hasPattern(s): addPattern(c, s)
 
 proc importSymbol(c: PContext, n: PNode, fromMod: PSym) =
-  let ident = lookups.considerQuotedIdent(n)
+  let ident = lookups.considerQuotedIdent(c.config, n)
   let s = strTableGet(fromMod.tab, ident)
   if s == nil:
     errorUndeclaredIdentifier(c, n.info, ident.s)
   else:
     if s.kind == skStub: loadStub(s)
     if s.kind notin ExportableSymKinds:
-      internalError(n.info, "importSymbol: 2")
+      internalError(c.config, n.info, "importSymbol: 2")
     # for an enumeration we have to add all identifiers
     case s.kind
     of skProcKinds:
@@ -77,7 +84,7 @@ proc importSymbol(c: PContext, n: PNode, fromMod: PSym) =
       var it: TIdentIter
       var e = initIdentIter(it, fromMod.tab, s.name)
       while e != nil:
-        if e.name.id != s.name.id: internalError(n.info, "importSymbol: 3")
+        if e.name.id != s.name.id: internalError(c.config, n.info, "importSymbol: 3")
         rawImportSymbol(c, e)
         e = nextIdentIter(it, fromMod.tab)
     else: rawImportSymbol(c, s)
@@ -89,7 +96,7 @@ proc importAllSymbolsExcept(c: PContext, fromMod: PSym, exceptSet: IntSet) =
     if s.kind != skModule:
       if s.kind != skEnumField:
         if s.kind notin ExportableSymKinds:
-          internalError(s.info, "importAllSymbols: " & $s.kind)
+          internalError(c.config, s.info, "importAllSymbols: " & $s.kind)
         if exceptSet.isNil or s.name.id notin exceptSet:
           rawImportSymbol(c, s)
     s = nextIter(i, fromMod.tab)
@@ -110,22 +117,23 @@ proc importForwarded(c: PContext, n: PNode, exceptSet: IntSet) =
       elif exceptSet.isNil or s.name.id notin exceptSet:
         rawImportSymbol(c, s)
   of nkExportExceptStmt:
-    localError(n.info, errGenerated, "'export except' not implemented")
+    localError(c.config, n.info, "'export except' not implemented")
   else:
     for i in 0..safeLen(n)-1:
       importForwarded(c, n.sons[i], exceptSet)
 
-proc importModuleAs(n: PNode, realModule: PSym): PSym =
+proc importModuleAs(c: PContext; n: PNode, realModule: PSym): PSym =
   result = realModule
   if n.kind != nkImportAs: discard
   elif n.len != 2 or n.sons[1].kind != nkIdent:
-    localError(n.info, errGenerated, "module alias must be an identifier")
+    localError(c.config, n.info, "module alias must be an identifier")
   elif n.sons[1].ident.id != realModule.name.id:
     # some misguided guy will write 'import abc.foo as foo' ...
-    result = createModuleAlias(realModule, n.sons[1].ident, realModule.info)
+    result = createModuleAlias(realModule, n.sons[1].ident, realModule.info,
+                               c.config.options)
 
 proc myImportModule(c: PContext, n: PNode): PSym =
-  var f = checkModuleName(n)
+  var f = checkModuleName(c.config, n)
   if f != InvalidFileIDX:
     let L = c.graph.importStack.len
     let recursion = c.graph.importStack.find(f)
@@ -138,7 +146,7 @@ proc myImportModule(c: PContext, n: PNode): PSym =
         err.add toFullPath(c.graph.importStack[i]) & " imports " &
                 toFullPath(c.graph.importStack[i+1])
       c.recursiveDep = err
-    result = importModuleAs(n, gImportModule(c.graph, c.module, f, c.cache))
+    result = importModuleAs(c, n, gImportModule(c.graph, c.module, f, c.cache))
     #echo "set back to ", L
     c.graph.importStack.setLen(L)
     # we cannot perform this check reliably because of
@@ -146,13 +154,13 @@ proc myImportModule(c: PContext, n: PNode): PSym =
     when true:
       if result.info.fileIndex == c.module.info.fileIndex and
           result.info.fileIndex == n.info.fileIndex:
-        localError(n.info, errGenerated, "A module cannot import itself")
+        localError(c.config, n.info, "A module cannot import itself")
     if sfDeprecated in result.flags:
       if result.constraint != nil:
-        message(n.info, warnDeprecated, result.constraint.strVal & "; " & result.name.s)
+        message(c.config, n.info, warnDeprecated, result.constraint.strVal & "; " & result.name.s)
       else:
-        message(n.info, warnDeprecated, result.name.s)
-    suggestSym(n.info, result, c.graph.usageSym, false)
+        message(c.config, n.info, warnDeprecated, result.name.s)
+    suggestSym(c.config, n.info, result, c.graph.usageSym, false)
 
 proc impMod(c: PContext; it: PNode) =
   let m = myImportModule(c, it)
@@ -182,7 +190,7 @@ proc evalImport(c: PContext, n: PNode): PNode =
 
 proc evalFrom(c: PContext, n: PNode): PNode =
   result = n
-  checkMinSonsLen(n, 2)
+  checkMinSonsLen(n, 2, c.config)
   var m = myImportModule(c, n.sons[0])
   if m != nil:
     n.sons[0] = newSymNode(m)
@@ -193,14 +201,10 @@ proc evalFrom(c: PContext, n: PNode): PNode =
 
 proc evalImportExcept*(c: PContext, n: PNode): PNode =
   result = n
-  checkMinSonsLen(n, 2)
+  checkMinSonsLen(n, 2, c.config)
   var m = myImportModule(c, n.sons[0])
   if m != nil:
     n.sons[0] = newSymNode(m)
     addDecl(c, m, n.info)               # add symbol to symbol table of module
-    var exceptSet = initIntSet()
-    for i in countup(1, sonsLen(n) - 1):
-      let ident = lookups.considerQuotedIdent(n.sons[i])
-      exceptSet.incl(ident.id)
-    importAllSymbolsExcept(c, m, exceptSet)
+    importAllSymbolsExcept(c, m, readExceptSet(c, n))
     #importForwarded(c, m.ast, exceptSet)
diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim
index da5267b93..25b554f7b 100644
--- a/compiler/jsgen.nim
+++ b/compiler/jsgen.nim
@@ -8,7 +8,6 @@
 #
 
 # This is the JavaScript code generator.
-# Also a PHP code generator. ;-)
 
 discard """
 The JS code generator contains only 2 tricks:
@@ -33,16 +32,15 @@ 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
+  intsets, cgmeth, lowerings, sighashes, configuration
 
 from modulegraphs import ModuleGraph
 
 type
-  TTarget = enum
-    targetJS, targetPHP
   TJSGen = object of TPassContext
     module: PSym
-    target: TTarget
+    graph: ModuleGraph
+    config: ConfigRef
     sigConflicts: CountTable[SigHash]
 
   BModule = ref TJSGen
@@ -92,24 +90,20 @@ type
     module: BModule
     g: PGlobals
     beforeRetNeeded: bool
-    target: TTarget # duplicated here for faster dispatching
     unique: int    # for temp identifier generation
     blocks: seq[TBlock]
     extraIndent: int
     up: PProc     # up the call chain; required for closure support
     declaredGlobals: IntSet
 
-template `|`(a, b: untyped): untyped {.dirty.} =
-  (if p.target == targetJS: a else: b)
-
-var indent = "\t".rope
+template config*(p: PProc): ConfigRef = p.module.config
 
 proc indentLine(p: PProc, r: Rope): Rope =
   result = r
   var p = p
   while true:
     for i in countup(0, p.blocks.len - 1 + p.extraIndent):
-      prepend(result, indent)
+      prepend(result, "\t".rope)
     if p.up == nil or p.up.prc != p.prc.owner:
       break
     p = p.up
@@ -157,11 +151,8 @@ proc newProc(globals: PGlobals, module: BModule, procDef: PNode,
     module: module,
     procDef: procDef,
     g: globals,
-    target: module.target,
     extraIndent: int(procDef != nil))
   if procDef != nil: result.prc = procDef.sons[namePos].sym
-  if result.target == targetPHP:
-    result.declaredGlobals = initIntSet()
 
 proc declareGlobal(p: PProc; id: int; r: Rope) =
   if p.prc != nil and not p.declaredGlobals.containsOrIncl(id):
@@ -205,11 +196,10 @@ proc mapType(typ: PType): TJSTypeKind =
     else: result = etyNone
   of tyProc: result = etyProc
   of tyCString: result = etyString
-  of tyUnused, tyOptAsRef: internalError("mapType")
+  of tyUnused, tyOptAsRef: doAssert(false, "mapType")
 
 proc mapType(p: PProc; typ: PType): TJSTypeKind =
-  if p.target == targetPHP: result = etyObject
-  else: result = mapType(typ)
+  result = mapType(typ)
 
 proc mangleName(m: BModule, s: PSym): Rope =
   proc validJsName(name: string): bool =
@@ -236,7 +226,7 @@ proc mangleName(m: BModule, s: PSym): Rope =
   if result == nil:
     if s.kind == skField and s.name.s.validJsName:
       result = rope(s.name.s)
-    elif m.target == targetJS or s.kind == skTemp:
+    elif s.kind == skTemp:
       result = rope(mangle(s.name.s))
     else:
       var x = newStringOfCap(s.name.s.len)
@@ -255,7 +245,7 @@ proc mangleName(m: BModule, s: PSym): Rope =
         inc i
       result = rope(x)
     if s.name.s != "this" and s.kind != skField:
-      if optHotCodeReloading in gOptions:
+      if optHotCodeReloading in m.config.options:
         # When hot reloading is enabled, we must ensure that the names
         # of functions and types will be preserved across rebuilds:
         add(result, idOrSig(s, m.module.name.s, m.sigConflicts))
@@ -298,23 +288,22 @@ proc genConstant(p: PProc, c: PSym)
 
 proc useMagic(p: PProc, name: string) =
   if name.len == 0: return
-  var s = magicsys.getCompilerProc(name)
+  var s = magicsys.getCompilerProc(p.module.graph, name)
   if s != nil:
-    internalAssert s.kind in {skProc, skFunc, skMethod, skConverter}
+    internalAssert p.config, s.kind in {skProc, skFunc, skMethod, skConverter}
     if not p.g.generatedSyms.containsOrIncl(s.id):
       let code = genProc(p, s)
       add(p.g.constants, code)
   else:
-    # we used to exclude the system module from this check, but for DLL
-    # generation support this sloppyness leads to hard to detect bugs, so
-    # we're picky here for the system module too:
-    if p.prc != nil: globalError(p.prc.info, errSystemNeeds, name)
-    else: rawMessage(errSystemNeeds, name)
+    if p.prc != nil:
+      globalError(p.config, p.prc.info, "system module needs: " & name)
+    else:
+      rawMessage(p.config, errGenerated, "system module needs: " & name)
 
 proc isSimpleExpr(p: PProc; n: PNode): bool =
   # calls all the way down --> can stay expression based
   if n.kind in nkCallKinds+{nkBracketExpr, nkDotExpr, nkPar, nkTupleConstr} or
-      (p.target == targetJS and n.kind in {nkObjConstr, nkBracket, nkCurly}):
+      (n.kind in {nkObjConstr, nkBracket, nkCurly}):
     for c in n:
       if not p.isSimpleExpr(c): return false
     result = true
@@ -323,12 +312,9 @@ proc isSimpleExpr(p: PProc; n: PNode): bool =
 
 proc getTemp(p: PProc, defineInLocals: bool = true): Rope =
   inc(p.unique)
-  if p.target == targetJS:
-    result = "Tmp$1" % [rope(p.unique)]
-    if defineInLocals:
-      add(p.locals, p.indentLine("var $1;$n" % [result]))
-  else:
-    result = "$$Tmp$1" % [rope(p.unique)]
+  result = "Tmp$1" % [rope(p.unique)]
+  if defineInLocals:
+    add(p.locals, p.indentLine("var $1;$n" % [result]))
 
 proc genAnd(p: PProc, a, b: PNode, r: var TCompRes) =
   assert r.kind == resNone
@@ -477,15 +463,9 @@ proc unsignedTrimmerJS(size: BiggestInt): Rope =
   of 4: rope">>> 0"
   else: rope""
 
-proc unsignedTrimmerPHP(size: BiggestInt): Rope =
-  case size
-  of 1: rope"& 0xff"
-  of 2: rope"& 0xffff"
-  of 4: rope"& 0xffffffff"
-  else: rope""
 
 template unsignedTrimmer(size: BiggestInt): Rope =
-  size.unsignedTrimmerJS | size.unsignedTrimmerPHP
+  size.unsignedTrimmerJS
 
 proc binaryUintExpr(p: PProc, n: PNode, r: var TCompRes, op: string,
                     reassign = false) =
@@ -533,46 +513,18 @@ proc arith(p: PProc, n: PNode, r: var TCompRes, op: TMagic) =
   of mMulU: binaryUintExpr(p, n, r, "*")
   of mDivU: binaryUintExpr(p, n, r, "/")
   of mDivI:
-    if p.target == targetPHP:
-      var x, y: TCompRes
-      gen(p, n.sons[1], x)
-      gen(p, n.sons[2], y)
-      r.res = "intval($1 / $2)" % [x.rdLoc, y.rdLoc]
-    else:
-      arithAux(p, n, r, op)
+    arithAux(p, n, r, op)
   of mModI:
-    if p.target == targetPHP:
-      var x, y: TCompRes
-      gen(p, n.sons[1], x)
-      gen(p, n.sons[2], y)
-      r.res = "($1 % $2)" % [x.rdLoc, y.rdLoc]
-    else:
-      arithAux(p, n, r, op)
+    arithAux(p, n, r, op)
   of mShrI:
     var x, y: TCompRes
     gen(p, n.sons[1], x)
     gen(p, n.sons[2], y)
     let trimmer = unsignedTrimmer(n[1].typ.skipTypes(abstractRange).size)
-    if p.target == targetPHP:
-      # XXX prevent multi evaluations
-      r.res = "(($1 $2) >= 0) ? (($1 $2) >> $3) : ((($1 $2) & 0x7fffffff) >> $3) | (0x40000000 >> ($3 - 1))" % [x.rdLoc, trimmer, y.rdLoc]
-    else:
-      r.res = "(($1 $2) >>> $3)" % [x.rdLoc, trimmer, y.rdLoc]
+    r.res = "(($1 $2) >>> $3)" % [x.rdLoc, trimmer, y.rdLoc]
   of mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr,
       mCStrToStr, mStrToStr, mEnumToStr:
-    if p.target == targetPHP:
-      if op == mEnumToStr:
-        var x: TCompRes
-        gen(p, n.sons[1], x)
-        r.res = "$#[$#]" % [genEnumInfoPHP(p, n.sons[1].typ), x.rdLoc]
-      elif op == mCharToStr:
-        var x: TCompRes
-        gen(p, n.sons[1], x)
-        r.res = "chr($#)" % [x.rdLoc]
-      else:
-        gen(p, n.sons[1], r)
-    else:
-      arithAux(p, n, r, op)
+    arithAux(p, n, r, op)
   else:
     arithAux(p, n, r, op)
   r.kind = resExpr
@@ -591,12 +543,12 @@ proc genLineDir(p: PProc, n: PNode) =
     useMagic(p, "endb")
     lineF(p, "endb($1);$n", [rope(line)])
   elif hasFrameInfo(p):
-    lineF(p, "F.line = $1;$n" | "$$F['line'] = $1;$n", [rope(line)])
+    lineF(p, "F.line = $1;$n", [rope(line)])
 
 proc genWhileStmt(p: PProc, n: PNode) =
   var
     cond: TCompRes
-  internalAssert isEmptyType(n.typ)
+  internalAssert p.config, isEmptyType(n.typ)
   genLineDir(p, n)
   inc(p.unique)
   var length = len(p.blocks)
@@ -604,12 +556,12 @@ proc genWhileStmt(p: PProc, n: PNode) =
   p.blocks[length].id = -p.unique
   p.blocks[length].isLoop = true
   let labl = p.unique.rope
-  lineF(p, "L$1: while (true) {$n" | "while (true) {$n", [labl])
+  lineF(p, "L$1: while (true) {$n", [labl])
   p.nested: gen(p, n.sons[0], cond)
-  lineF(p, "if (!$1) break L$2;$n" | "if (!$1) goto L$2;$n",
+  lineF(p, "if (!$1) break L$2;$n",
        [cond.res, labl])
   p.nested: genStmt(p, n.sons[1])
-  lineF(p, "}$n" | "}L$#:;$n", [labl])
+  lineF(p, "}$n", [labl])
   setLen(p.blocks, length)
 
 proc moveInto(p: PProc, src: var TCompRes, dest: TCompRes) =
@@ -653,26 +605,22 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) =
   var i = 1
   var length = sonsLen(n)
   var catchBranchesExist = length > 1 and n.sons[i].kind == nkExceptBranch
-  if catchBranchesExist and p.target == targetJS:
+  if catchBranchesExist:
     add(p.body, "++excHandler;" & tnl)
   var tmpFramePtr = rope"F"
   if optStackTrace notin p.options:
     tmpFramePtr = p.getTemp(true)
     line(p, tmpFramePtr & " = framePtr;" & tnl)
   lineF(p, "try {$n", [])
-  if p.target == targetPHP and p.globals == nil:
-      p.globals = "global $lastJSError; global $prevJSError;".rope
   var a: TCompRes
   gen(p, n.sons[0], a)
   moveInto(p, a, r)
   var generalCatchBranchExists = false
-  let dollar = rope(if p.target == targetJS: "" else: "$")
-  if p.target == targetJS and catchBranchesExist:
+  let dollar = rope("")
+  if catchBranchesExist:
     addf(p.body, "--excHandler;$n} catch (EXC) {$n var prevJSError = lastJSError;$n" &
         " lastJSError = EXC;$n --excHandler;$n", [])
     line(p, "framePtr = $1;$n" % [tmpFramePtr])
-  elif p.target == targetPHP:
-    lineF(p, "} catch (Exception $$EXC) {$n $$prevJSError = $$lastJSError;$n $$lastJSError = $$EXC;$n", [])
   while i < length and n.sons[i].kind == nkExceptBranch:
     let blen = sonsLen(n.sons[i])
     if blen == 1:
@@ -687,7 +635,7 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) =
       useMagic(p, "isObj")
       for j in countup(0, blen - 2):
         if n.sons[i].sons[j].kind != nkType:
-          internalError(n.info, "genTryStmt")
+          internalError(p.config, n.info, "genTryStmt")
         if orExpr != nil: add(orExpr, "||")
         addf(orExpr, "isObj($2lastJSError.m_type, $1)",
              [genTypeInfo(p, n.sons[i].sons[j].typ), dollar])
@@ -701,22 +649,14 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) =
     if not generalCatchBranchExists:
       useMagic(p, "reraiseException")
       line(p, "else {" & tnl)
-      line(p, indent & "reraiseException();" & tnl)
+      line(p, "\treraiseException();" & tnl)
       line(p, "}" & tnl)
     addf(p.body, "$1lastJSError = $1prevJSError;$n", [dollar])
-  if p.target == targetJS:
-    line(p, "} finally {" & tnl)
-    line(p, "framePtr = $1;$n" % [tmpFramePtr])
-  if p.target == targetPHP:
-    # XXX ugly hack for PHP codegen
-    line(p, "}" & tnl)
+  line(p, "} finally {" & tnl)
+  line(p, "framePtr = $1;$n" % [tmpFramePtr])
   if i < length and n.sons[i].kind == nkFinally:
     genStmt(p, n.sons[i].sons[0])
-  if p.target == targetPHP:
-    # XXX ugly hack for PHP codegen
-    line(p, "if($lastJSError) throw($lastJSError);" & tnl)
-  if p.target == targetJS:
-    line(p, "}" & tnl)
+  line(p, "}" & tnl)
 
 proc genRaiseStmt(p: PProc, n: PNode) =
   genLineDir(p, n)
@@ -737,7 +677,7 @@ proc genCaseJS(p: PProc, n: PNode, r: var TCompRes) =
   genLineDir(p, n)
   gen(p, n.sons[0], cond)
   let stringSwitch = skipTypes(n.sons[0].typ, abstractVar).kind == tyString
-  if stringSwitch and p.target == targetJS:
+  if stringSwitch:
     useMagic(p, "toJSStr")
     lineF(p, "switch (toJSStr($1)) {$n", [cond.rdLoc])
   else:
@@ -762,7 +702,7 @@ proc genCaseJS(p: PProc, n: PNode, r: var TCompRes) =
             case e.kind
             of nkStrLit..nkTripleStrLit: lineF(p, "case $1:$n",
                 [makeJSString(e.strVal, false)])
-            else: internalError(e.info, "jsgen.genCaseStmt: 2")
+            else: internalError(p.config, e.info, "jsgen.genCaseStmt: 2")
           else:
             gen(p, e, cond)
             lineF(p, "case $1:$n", [cond.rdLoc])
@@ -776,7 +716,7 @@ proc genCaseJS(p: PProc, n: PNode, r: var TCompRes) =
         gen(p, it.sons[0], stmt)
         moveInto(p, stmt, r)
         lineF(p, "break;$n", [])
-    else: internalError(it.info, "jsgen.genCaseStmt")
+    else: internalError(p.config, it.info, "jsgen.genCaseStmt")
   lineF(p, "}$n", [])
 
 proc genBlock(p: PProc, n: PNode, r: var TCompRes) =
@@ -784,17 +724,17 @@ proc genBlock(p: PProc, n: PNode, r: var TCompRes) =
   let idx = len(p.blocks)
   if n.sons[0].kind != nkEmpty:
     # named block?
-    if (n.sons[0].kind != nkSym): internalError(n.info, "genBlock")
+    if (n.sons[0].kind != nkSym): internalError(p.config, n.info, "genBlock")
     var sym = n.sons[0].sym
     sym.loc.k = locOther
     sym.position = idx+1
   let labl = p.unique
-  lineF(p, "L$1: do {$n" | "", [labl.rope])
+  lineF(p, "L$1: do {$n", [labl.rope])
   setLen(p.blocks, idx + 1)
   p.blocks[idx].id = - p.unique # negative because it isn't used yet
   gen(p, n.sons[1], r)
   setLen(p.blocks, idx)
-  lineF(p, "} while(false);$n" | "$nL$#:;$n", [labl.rope])
+  lineF(p, "} while(false);$n", [labl.rope])
 
 proc genBreakStmt(p: PProc, n: PNode) =
   var idx: int
@@ -810,9 +750,9 @@ proc genBreakStmt(p: PProc, n: PNode) =
     idx = len(p.blocks) - 1
     while idx >= 0 and not p.blocks[idx].isLoop: dec idx
     if idx < 0 or not p.blocks[idx].isLoop:
-      internalError(n.info, "no loop to break")
+      internalError(p.config, n.info, "no loop to break")
   p.blocks[idx].id = abs(p.blocks[idx].id) # label is used
-  lineF(p, "break L$1;$n" | "goto L$1;$n", [rope(p.blocks[idx].id)])
+  lineF(p, "break L$1;$n", [rope(p.blocks[idx].id)])
 
 proc genAsmOrEmitStmt(p: PProc, n: PNode) =
   genLineDir(p, n)
@@ -826,7 +766,6 @@ proc genAsmOrEmitStmt(p: PProc, n: PNode) =
       let v = it.sym
       # for backwards compatibility we don't deref syms here :-(
       if v.kind in {skVar, skLet, skTemp, skConst, skResult, skParam, skForVar}:
-        if p.target == targetPHP: p.body.add "$"
         p.body.add mangleName(p.module, v)
       else:
         var r: TCompRes
@@ -869,24 +808,11 @@ proc generateHeader(p: PProc, typ: PType): Rope =
     if isCompileTimeOnly(param.typ): continue
     if result != nil: add(result, ", ")
     var name = mangleName(p.module, param)
-    if p.target == targetJS:
-      add(result, name)
-      if mapType(param.typ) == etyBaseIndex:
-        add(result, ", ")
-        add(result, name)
-        add(result, "_Idx")
-    elif not (i == 1 and param.name.s == "this"):
-      let k = param.typ.skipTypes({tyGenericInst, tyAlias, tySink}).kind
-      if k in {tyVar, tyRef, tyPtr, tyLent, tyPointer}:
-        add(result, "&")
-      add(result, "$")
+    add(result, name)
+    if mapType(param.typ) == etyBaseIndex:
+      add(result, ", ")
       add(result, name)
-      # XXX I think something like this is needed for PHP to really support
-      # ptr "inside" strings and seq
-      #if mapType(param.typ) == etyBaseIndex:
-      #  add(result, ", $")
-      #  add(result, name)
-      #  add(result, "_Idx")
+      add(result, "_Idx")
 
 proc countJsParams(typ: PType): int =
   for i in countup(1, sonsLen(typ.n) - 1):
@@ -906,21 +832,10 @@ const
 
 proc needsNoCopy(p: PProc; y: PNode): bool =
   result = (y.kind in nodeKindsNeedNoCopy) or
-      (skipTypes(y.typ, abstractInst).kind in {tyRef, tyPtr, tyLent, tyVar}) or
-      p.target == targetPHP
+      (skipTypes(y.typ, abstractInst).kind in {tyRef, tyPtr, tyLent, tyVar})
 
 proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) =
   var a, b: TCompRes
-
-  if p.target == targetPHP and x.kind == nkBracketExpr and
-      x[0].typ.skipTypes(abstractVar).kind in {tyString, tyCString}:
-    var c: TCompRes
-    gen(p, x[0], a)
-    gen(p, x[1], b)
-    gen(p, y, c)
-    lineF(p, "$#[$#] = chr($#);$n", [a.rdLoc, b.rdLoc, c.rdLoc])
-    return
-
   var xtyp = mapType(p, x.typ)
 
   if x.kind == nkHiddenDeref and x.sons[0].kind == nkCall and xtyp != etyObject:
@@ -960,7 +875,7 @@ proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) =
       elif b.typ == etyBaseIndex:
         lineF(p, "$# = $#;$n", [a.res, b.rdLoc])
       else:
-        internalError(x.info, "genAsgn")
+        internalError(p.config, x.info, "genAsgn")
     else:
       lineF(p, "$1 = $2; $3 = $4;$n", [a.address, b.address, a.res, b.res])
   else:
@@ -968,7 +883,7 @@ proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) =
 
 proc genAsgn(p: PProc, n: PNode) =
   genLineDir(p, n)
-  genAsgnAux(p, n.sons[0], n.sons[1], noCopyNeeded=p.target == targetPHP)
+  genAsgnAux(p, n.sons[0], n.sons[1], noCopyNeeded=false)
 
 proc genFastAsgn(p: PProc, n: PNode) =
   genLineDir(p, n)
@@ -990,20 +905,18 @@ proc genSwap(p: PProc, n: PNode) =
   if mapType(p, skipTypes(n.sons[1].typ, abstractVar)) == etyBaseIndex:
     let tmp2 = p.getTemp(false)
     if a.typ != etyBaseIndex or b.typ != etyBaseIndex:
-      internalError(n.info, "genSwap")
-    lineF(p, "var $1 = $2; $2 = $3; $3 = $1;$n" |
-             "$1 = $2; $2 = $3; $3 = $1;$n",
+      internalError(p.config, n.info, "genSwap")
+    lineF(p, "var $1 = $2; $2 = $3; $3 = $1;$n",
              [tmp, a.address, b.address])
     tmp = tmp2
-  lineF(p, "var $1 = $2; $2 = $3; $3 = $1;" |
-           "$1 = $2; $2 = $3; $3 = $1;",
+  lineF(p, "var $1 = $2; $2 = $3; $3 = $1;",
            [tmp, a.res, b.res])
 
-proc getFieldPosition(f: PNode): int =
+proc getFieldPosition(p: PProc; f: PNode): int =
   case f.kind
   of nkIntLit..nkUInt64Lit: result = int(f.intVal)
   of nkSym: result = f.sym.position
-  else: internalError(f.info, "genFieldPosition")
+  else: internalError(p.config, f.info, "genFieldPosition")
 
 proc genFieldAddr(p: PProc, n: PNode, r: var TCompRes) =
   var a: TCompRes
@@ -1011,16 +924,13 @@ proc genFieldAddr(p: PProc, n: PNode, r: var TCompRes) =
   let b = if n.kind == nkHiddenAddr: n.sons[0] else: n
   gen(p, b.sons[0], a)
   if skipTypes(b.sons[0].typ, abstractVarRange).kind == tyTuple:
-    if p.target == targetJS:
-      r.res = makeJSString( "Field" & $getFieldPosition(b.sons[1]) )
-    else:
-      r.res = getFieldPosition(b.sons[1]).rope
+    r.res = makeJSString("Field" & $getFieldPosition(p, b.sons[1]))
   else:
-    if b.sons[1].kind != nkSym: internalError(b.sons[1].info, "genFieldAddr")
+    if b.sons[1].kind != nkSym: internalError(p.config, b.sons[1].info, "genFieldAddr")
     var f = b.sons[1].sym
     if f.loc.r == nil: f.loc.r = mangleName(p.module, f)
     r.res = makeJSString($f.loc.r)
-  internalAssert a.typ != etyBaseIndex
+  internalAssert p.config, a.typ != etyBaseIndex
   r.address = a.res
   r.kind = resExpr
 
@@ -1029,26 +939,20 @@ proc genFieldAccess(p: PProc, n: PNode, r: var TCompRes) =
   gen(p, n.sons[0], r)
   let otyp = skipTypes(n.sons[0].typ, abstractVarRange)
   if otyp.kind == tyTuple:
-    r.res = ("$1.Field$2" | "$1[$2]") %
-        [r.res, getFieldPosition(n.sons[1]).rope]
+    r.res = ("$1.Field$2") %
+        [r.res, getFieldPosition(p, n.sons[1]).rope]
   else:
-    if n.sons[1].kind != nkSym: internalError(n.sons[1].info, "genFieldAccess")
+    if n.sons[1].kind != nkSym: internalError(p.config, n.sons[1].info, "genFieldAccess")
     var f = n.sons[1].sym
     if f.loc.r == nil: f.loc.r = mangleName(p.module, f)
-    if p.target == targetJS:
-      r.res = "$1.$2" % [r.res, f.loc.r]
-    else:
-      if {sfImportc, sfExportc} * f.flags != {}:
-        r.res = "$1->$2" % [r.res, f.loc.r]
-      else:
-        r.res = "$1['$2']" % [r.res, f.loc.r]
+    r.res = "$1.$2" % [r.res, f.loc.r]
   r.kind = resExpr
 
 proc genAddr(p: PProc, n: PNode, r: var TCompRes)
 
 proc genCheckedFieldAddr(p: PProc, n: PNode, r: var TCompRes) =
   let m = if n.kind == nkHiddenAddr: n.sons[0] else: n
-  internalAssert m.kind == nkCheckedFieldExpr
+  internalAssert p.config, m.kind == nkCheckedFieldExpr
   genAddr(p, m, r) # XXX
 
 proc genCheckedFieldAccess(p: PProc, n: PNode, r: var TCompRes) =
@@ -1062,20 +966,14 @@ proc genArrayAddr(p: PProc, n: PNode, r: var TCompRes) =
   let m = if n.kind == nkHiddenAddr: n.sons[0] else: n
   gen(p, m.sons[0], a)
   gen(p, m.sons[1], b)
-  internalAssert a.typ != etyBaseIndex and b.typ != etyBaseIndex
+  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])
   else: first = 0
   if optBoundsCheck in p.options:
     useMagic(p, "chckIndx")
-    if p.target == targetPHP:
-      if typ.kind != tyString:
-        r.res = "chckIndx($1, $2, count($3)-1)-$2" % [b.res, rope(first), a.res]
-      else:
-        r.res = "chckIndx($1, $2, strlen($3))-$2" % [b.res, rope(first), a.res]
-    else:
-      r.res = "chckIndx($1, $2, $3.length+$2-1)-$2" % [b.res, rope(first), a.res]
+    r.res = "chckIndx($1, $2, $3.length+$2-1)-$2" % [b.res, rope(first), a.res]
   elif first != 0:
     r.res = "($1)-$2" % [b.res, rope(first)]
   else:
@@ -1089,27 +987,11 @@ proc genArrayAccess(p: PProc, n: PNode, r: var TCompRes) =
   of tyArray, tyOpenArray, tySequence, tyString, tyCString, tyVarargs:
     genArrayAddr(p, n, r)
   of tyTuple:
-    if p.target == targetPHP:
-      genFieldAccess(p, n, r)
-      return
     genFieldAddr(p, n, r)
-  else: internalError(n.info, "expr(nkBracketExpr, " & $ty.kind & ')')
+  else: internalError(p.config, n.info, "expr(nkBracketExpr, " & $ty.kind & ')')
   r.typ = etyNone
-  if r.res == nil: internalError(n.info, "genArrayAccess")
-  if p.target == targetPHP:
-    if n.sons[0].kind in nkCallKinds+{nkStrLit..nkTripleStrLit}:
-      useMagic(p, "nimAt")
-      if ty.kind in {tyString, tyCString}:
-        # XXX this needs to be more like substr($1,$2)
-        r.res = "ord(nimAt($1, $2))" % [r.address, r.res]
-      else:
-        r.res = "nimAt($1, $2)" % [r.address, r.res]
-    elif ty.kind in {tyString, tyCString}:
-      # XXX this needs to be more like substr($1,$2)
-      r.res = "ord(@$1[$2])" % [r.address, r.res]
-    else:
-      r.res = "$1[$2]" % [r.address, r.res]
-  elif ty.kind == tyCString:
+  if r.res == nil: internalError(p.config, n.info, "genArrayAccess")
+  if ty.kind == tyCString:
     r.res = "$1.charCodeAt($2)" % [r.address, r.res]
   else:
     r.res = "$1[$2]" % [r.address, r.res]
@@ -1122,13 +1004,13 @@ template isIndirect(x: PSym): bool =
     #(mapType(v.typ) != etyObject) and
     {sfImportc, sfVolatile, sfExportc} * v.flags == {} and
     v.kind notin {skProc, skFunc, skConverter, skMethod, skIterator,
-                  skConst, skTemp, skLet} and p.target == targetJS)
+                  skConst, skTemp, skLet})
 
 proc genAddr(p: PProc, n: PNode, r: var TCompRes) =
   case n.sons[0].kind
   of nkSym:
     let s = n.sons[0].sym
-    if s.loc.r == nil: internalError(n.info, "genAddr: 3")
+    if s.loc.r == nil: internalError(p.config, n.info, "genAddr: 3")
     case s.kind
     of skVar, skLet, skResult:
       r.kind = resExpr
@@ -1138,8 +1020,6 @@ proc genAddr(p: PProc, n: PNode, r: var TCompRes) =
         r.typ = etyNone
         if isIndirect(s):
           r.res = s.loc.r & "[0]"
-        elif p.target == targetPHP:
-          r.res = "&" & s.loc.r
         else:
           r.res = s.loc.r
         r.address = nil
@@ -1152,8 +1032,8 @@ proc genAddr(p: PProc, n: PNode, r: var TCompRes) =
       else:
         # 'var openArray' for instance produces an 'addr' but this is harmless:
         gen(p, n.sons[0], r)
-        #internalError(n.info, "genAddr: 4 " & renderTree(n))
-    else: internalError(n.info, "genAddr: 2")
+        #internalError(p.config, n.info, "genAddr: 4 " & renderTree(n))
+    else: internalError(p.config, n.info, "genAddr: 2")
   of nkCheckedFieldExpr:
     genCheckedFieldAddr(p, n, r)
   of nkDotExpr:
@@ -1172,23 +1052,15 @@ proc genAddr(p: PProc, n: PNode, r: var TCompRes) =
         genArrayAddr(p, n.sons[0], r)
       of tyTuple:
         genFieldAddr(p, n.sons[0], r)
-      else: internalError(n.sons[0].info, "expr(nkBracketExpr, " & $kindOfIndexedExpr & ')')
+      else: internalError(p.config, n.sons[0].info, "expr(nkBracketExpr, " & $kindOfIndexedExpr & ')')
   of nkObjDownConv:
     gen(p, n.sons[0], r)
   of nkHiddenDeref:
     gen(p, n.sons[0].sons[0], r)
-  else: internalError(n.sons[0].info, "genAddr: " & $n.sons[0].kind)
+  else: internalError(p.config, n.sons[0].info, "genAddr: " & $n.sons[0].kind)
 
 proc thisParam(p: PProc; typ: PType): PType =
-  if p.target == targetPHP:
-    # XXX Might be very useful for the JS backend too?
-    let typ = skipTypes(typ, abstractInst)
-    assert(typ.kind == tyProc)
-    if 1 < sonsLen(typ.n):
-      assert(typ.n.sons[1].kind == nkSym)
-      let param = typ.n.sons[1].sym
-      if param.name.s == "this":
-        result = param.typ.skipTypes(abstractVar)
+  discard
 
 proc attachProc(p: PProc; content: Rope; s: PSym) =
   let otyp = thisParam(p, s.typ)
@@ -1219,40 +1091,28 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) =
   case s.kind
   of skVar, skLet, skParam, skTemp, skResult, skForVar:
     if s.loc.r == nil:
-      internalError(n.info, "symbol has no generated name: " & s.name.s)
-    if p.target == targetJS:
-      let k = mapType(p, s.typ)
-      if k == etyBaseIndex:
-        r.typ = etyBaseIndex
-        if {sfAddrTaken, sfGlobal} * s.flags != {}:
-          r.address = "$1[0]" % [s.loc.r]
-          r.res = "$1[1]" % [s.loc.r]
-        else:
-          r.address = s.loc.r
-          r.res = s.loc.r & "_Idx"
-      elif isIndirect(s):
-        r.res = "$1[0]" % [s.loc.r]
+      internalError(p.config, n.info, "symbol has no generated name: " & s.name.s)
+    let k = mapType(p, s.typ)
+    if k == etyBaseIndex:
+      r.typ = etyBaseIndex
+      if {sfAddrTaken, sfGlobal} * s.flags != {}:
+        r.address = "$1[0]" % [s.loc.r]
+        r.res = "$1[1]" % [s.loc.r]
       else:
-        r.res = s.loc.r
+        r.address = s.loc.r
+        r.res = s.loc.r & "_Idx"
+    elif isIndirect(s):
+      r.res = "$1[0]" % [s.loc.r]
     else:
-      r.res = "$" & s.loc.r
-      if sfGlobal in s.flags:
-        p.declareGlobal(s.id, r.res)
+      r.res = s.loc.r
   of skConst:
     genConstant(p, s)
     if s.loc.r == nil:
-      internalError(n.info, "symbol has no generated name: " & s.name.s)
-    if p.target == targetJS:
-      r.res = s.loc.r
-    else:
-      r.res = "$" & s.loc.r
-      p.declareGlobal(s.id, r.res)
+      internalError(p.config, n.info, "symbol has no generated name: " & s.name.s)
+    r.res = s.loc.r
   of skProc, skFunc, skConverter, skMethod:
     discard mangleName(p.module, s)
-    if p.target == targetPHP and r.kind != resCallee:
-      r.res = makeJsString($s.loc.r)
-    else:
-      r.res = s.loc.r
+    r.res = s.loc.r
     if lfNoDecl in s.loc.flags or s.magic != mNone or
        {sfImportc, sfInfixCall} * s.flags != {}:
       discard
@@ -1265,7 +1125,7 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) =
       genProcForSymIfNeeded(p, s)
   else:
     if s.loc.r == nil:
-      internalError(n.info, "symbol has no generated name: " & s.name.s)
+      internalError(p.config, n.info, "symbol has no generated name: " & s.name.s)
     r.res = s.loc.r
   r.kind = resVal
 
@@ -1286,7 +1146,7 @@ proc genDeref(p: PProc, n: PNode, r: var TCompRes) =
     elif t == etyBaseIndex:
       r.res = "$1[0]" % [a.res]
     else:
-      internalError(n.info, "genDeref")
+      internalError(p.config, n.info, "genDeref")
 
 proc genArgNoParam(p: PProc, n: PNode, r: var TCompRes) =
   var a: TCompRes
@@ -1346,14 +1206,14 @@ proc genArgs(p: PProc, n: PNode, r: var TCompRes; start=1) =
     # XXX look into this:
     let jsp = countJsParams(typ)
     if emitted != jsp and tfVarargs notin typ.flags:
-      localError(n.info, "wrong number of parameters emitted; expected: " & $jsp &
+      localError(p.config, n.info, "wrong number of parameters emitted; expected: " & $jsp &
         " but got: " & $emitted)
   r.kind = resExpr
 
 proc genOtherArg(p: PProc; n: PNode; i: int; typ: PType;
                  generated: var int; r: var TCompRes) =
   if i >= n.len:
-    globalError(n.info, "wrong importcpp pattern; expected parameter at position " & $i &
+    globalError(p.config, n.info, "wrong importcpp pattern; expected parameter at position " & $i &
         " but got only: " & $(n.len-1))
   let it = n[i]
   var paramType: PNode = nil
@@ -1407,7 +1267,7 @@ proc genInfixCall(p: PProc, n: PNode, r: var TCompRes) =
   if f.loc.r == nil: f.loc.r = mangleName(p.module, f)
   if sfInfixCall in f.flags:
     let pat = n.sons[0].sym.loc.r.data
-    internalAssert pat != nil
+    internalAssert p.config, pat != nil
     if pat.contains({'#', '(', '@'}):
       var typ = skipTypes(n.sons[0].typ, abstractInst)
       assert(typ.kind == tyProc)
@@ -1417,14 +1277,12 @@ proc genInfixCall(p: PProc, n: PNode, r: var TCompRes) =
     gen(p, n.sons[1], r)
     if r.typ == etyBaseIndex:
       if r.address == nil:
-        globalError(n.info, "cannot invoke with infix syntax")
+        globalError(p.config, n.info, "cannot invoke with infix syntax")
       r.res = "$1[$2]" % [r.address, r.res]
       r.address = nil
       r.typ = etyNone
-    add(r.res, "." | "->")
+    add(r.res, ".")
   var op: TCompRes
-  if p.target == targetPHP:
-    op.kind = resCallee
   gen(p, n.sons[0], op)
   add(r.res, op.res)
   genArgs(p, n, r, 2)
@@ -1433,28 +1291,21 @@ proc genCall(p: PProc, n: PNode, r: var TCompRes) =
   if n.sons[0].kind == nkSym and thisParam(p, n.sons[0].typ) != nil:
     genInfixCall(p, n, r)
     return
-  if p.target == targetPHP:
-    r.kind = resCallee
   gen(p, n.sons[0], r)
   genArgs(p, n, r)
 
 proc genEcho(p: PProc, n: PNode, r: var TCompRes) =
   let n = n[1].skipConv
-  internalAssert n.kind == nkBracket
-  if p.target == targetJS:
-    useMagic(p, "toJSStr") # Used in rawEcho
-    useMagic(p, "rawEcho")
-  elif n.len == 0:
-    r.kind = resExpr
-    add(r.res, """print("\n")""")
-    return
-  add(r.res, "rawEcho(" | "print(")
+  internalAssert p.config, n.kind == nkBracket
+  useMagic(p, "toJSStr") # Used in rawEcho
+  useMagic(p, "rawEcho")
+  add(r.res, "rawEcho(")
   for i in countup(0, sonsLen(n) - 1):
     let it = n.sons[i]
     if it.typ.isCompileTimeOnly: continue
-    if i > 0: add(r.res, ", " | ".")
+    if i > 0: add(r.res, ", ")
     genArgNoParam(p, it, r)
-  add(r.res, ")" | """."\n")""")
+  add(r.res, ")")
   r.kind = resExpr
 
 proc putToSeq(s: string, indirect: bool): Rope =
@@ -1474,18 +1325,15 @@ proc createRecordVarAux(p: PProc, rec: PNode, excludedFieldIDs: IntSet, output:
   of nkSym:
     if rec.sym.id notin excludedFieldIDs:
       if output.len > 0: output.add(", ")
-      if p.target == targetJS:
-        output.addf("$#: ", [mangleName(p.module, rec.sym)])
-      else:
-        output.addf("'$#' => ", [mangleName(p.module, rec.sym)])
+      output.addf("$#: ", [mangleName(p.module, rec.sym)])
       output.add(createVar(p, rec.sym.typ, false))
-  else: internalError(rec.info, "createRecordVarAux")
+  else: internalError(p.config, rec.info, "createRecordVarAux")
 
 proc createObjInitList(p: PProc, typ: PType, excludedFieldIDs: IntSet, output: var Rope) =
   var t = typ
   if objHasTypeField(t):
     if output.len > 0: output.add(", ")
-    addf(output, "m_type: $1" | "'m_type' => $#", [genTypeInfo(p, t)])
+    addf(output, "m_type: $1", [genTypeInfo(p, t)])
   while t != nil:
     t = t.skipTypes(skipPtrs)
     createRecordVarAux(p, t.n, excludedFieldIDs, output)
@@ -1514,49 +1362,42 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope =
   of tyRange, tyGenericInst, tyAlias, tySink:
     result = createVar(p, lastSon(typ), indirect)
   of tySet:
-    result = putToSeq("{}" | "array()", indirect)
+    result = putToSeq("{}", indirect)
   of tyBool:
     result = putToSeq("false", indirect)
   of tyArray:
     let length = int(lengthOrd(t))
     let e = elemType(t)
     let jsTyp = arrayTypeForElemType(e)
-    if not jsTyp.isNil and p.target == targetJS:
+    if not jsTyp.isNil:
       result = "new $1($2)" % [rope(jsTyp), rope(length)]
     elif length > 32:
       useMagic(p, "arrayConstr")
       # XXX: arrayConstr depends on nimCopy. This line shouldn't be necessary.
-      if p.target == targetJS: useMagic(p, "nimCopy")
+      useMagic(p, "nimCopy")
       result = "arrayConstr($1, $2, $3)" % [rope(length),
           createVar(p, e, false), genTypeInfo(p, e)]
     else:
-      result = rope("[" | "array(")
+      result = rope("[")
       var i = 0
       while i < length:
         if i > 0: add(result, ", ")
         add(result, createVar(p, e, false))
         inc(i)
-      add(result, "]" | ")")
+      add(result, "]")
     if indirect: result = "[$1]" % [result]
   of tyTuple:
-    if p.target == targetJS:
-      result = rope("{")
-      for i in 0..<t.sonsLen:
-        if i > 0: add(result, ", ")
-        addf(result, "Field$1: $2", [i.rope,
-             createVar(p, t.sons[i], false)])
-      add(result, "}")
-      if indirect: result = "[$1]" % [result]
-    else:
-      result = rope("array(")
-      for i in 0..<t.sonsLen:
-        if i > 0: add(result, ", ")
-        add(result, createVar(p, t.sons[i], false))
-      add(result, ")")
+    result = rope("{")
+    for i in 0..<t.sonsLen:
+      if i > 0: add(result, ", ")
+      addf(result, "Field$1: $2", [i.rope,
+            createVar(p, t.sons[i], false)])
+    add(result, "}")
+    if indirect: result = "[$1]" % [result]
   of tyObject:
     var initList: Rope
     createObjInitList(p, t, initIntSet(), initList)
-    result = ("{$1}" | "array($#)") % [initList]
+    result = ("{$1}") % [initList]
     if indirect: result = "[$1]" % [result]
   of tyVar, tyPtr, tyLent, tyRef:
     if mapType(p, t) == etyBaseIndex:
@@ -1569,10 +1410,10 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope =
     if t.n != nil:
       result = createVar(p, lastSon t, indirect)
     else:
-      internalError("createVar: " & $t.kind)
+      internalError(p.config, "createVar: " & $t.kind)
       result = nil
   else:
-    internalError("createVar: " & $t.kind)
+    internalError(p.config, "createVar: " & $t.kind)
     result = nil
 
 template returnType: untyped =
@@ -1584,7 +1425,7 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) =
     s: Rope
     varCode: string
     varName = mangleName(p.module, v)
-    useReloadingGuard = sfGlobal in v.flags and optHotCodeReloading in gOptions
+    useReloadingGuard = sfGlobal in v.flags and optHotCodeReloading in p.config.options
 
   if v.constraint.isNil:
     if useReloadingGuard:
@@ -1597,7 +1438,7 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) =
     varCode = v.constraint.strVal
 
   if n.kind == nkEmpty:
-    lineF(p, varCode & " = $3;$n" | "$$$2 = $3;$n",
+    lineF(p, varCode & " = $3;$n",
                [returnType, varName, createVar(p, v.typ, isIndirect(v))])
     if v.typ.kind in {tyVar, tyPtr, tyLent, tyRef} and mapType(p, v.typ) == etyBaseIndex:
       lineF(p, "var $1_Idx = 0;$n", [varName])
@@ -1632,7 +1473,7 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) =
     if isIndirect(v):
       lineF(p, varCode & " = [$3];$n", [returnType, v.loc.r, s])
     else:
-      lineF(p, varCode & " = $3;$n" | "$$$2 = $3;$n", [returnType, v.loc.r, s])
+      lineF(p, varCode & " = $3;$n", [returnType, v.loc.r, s])
 
   if useReloadingGuard:
     lineF(p, "}$n")
@@ -1642,7 +1483,7 @@ proc genVarStmt(p: PProc, n: PNode) =
     var a = n.sons[i]
     if a.kind != nkCommentStmt:
       if a.kind == nkVarTuple:
-        let unpacked = lowerTupleUnpacking(a, p.prc)
+        let unpacked = lowerTupleUnpacking(p.module.graph, a, p.prc)
         genStmt(p, unpacked)
       else:
         assert(a.kind == nkIdentDefs)
@@ -1665,25 +1506,21 @@ proc genNew(p: PProc, n: PNode) =
   var a: TCompRes
   gen(p, n.sons[1], a)
   var t = skipTypes(n.sons[1].typ, abstractVar).sons[0]
-  if p.target == targetJS:
-    lineF(p, "$1 = $2;$n", [a.res, createVar(p, t, false)])
-  else:
-    lineF(p, "$3 = $2; $1 = &$3;$n", [a.res, createVar(p, t, false), getTemp(p)])
+  lineF(p, "$1 = $2;$n", [a.res, createVar(p, t, false)])
 
 proc genNewSeq(p: PProc, n: PNode) =
   var x, y: TCompRes
   gen(p, n.sons[1], x)
   gen(p, n.sons[2], y)
   let t = skipTypes(n.sons[1].typ, abstractVar).sons[0]
-  lineF(p, "$1 = new Array($2); for (var i=0;i<$2;++i) {$1[i]=$3;}" |
-           "$1 = array(); for ($$i=0;$$i<$2;++$$i) {$1[]=$3;}", [
+  lineF(p, "$1 = new Array($2); for (var i=0;i<$2;++i) {$1[i]=$3;}", [
     x.rdLoc, y.rdLoc, createVar(p, t, false)])
 
 proc genOrd(p: PProc, n: PNode, r: var TCompRes) =
   case skipTypes(n.sons[1].typ, abstractVar).kind
   of tyEnum, tyInt..tyUInt64, tyChar: gen(p, n.sons[1], r)
   of tyBool: unaryExpr(p, n, r, "", "($1 ? 1:0)")
-  else: internalError(n.info, "genOrd")
+  else: internalError(p.config, n.info, "genOrd")
 
 proc genConStrStr(p: PProc, n: PNode, r: var TCompRes) =
   var a: TCompRes
@@ -1708,21 +1545,6 @@ proc genConStrStr(p: PProc, n: PNode, r: var TCompRes) =
   else:
     r.res.add("$1)" % [a.res])
 
-proc genConStrStrPHP(p: PProc, n: PNode, r: var TCompRes) =
-  var a: TCompRes
-  gen(p, n.sons[1], a)
-  r.kind = resExpr
-  if skipTypes(n.sons[1].typ, abstractVarRange).kind == tyChar:
-    r.res.add("chr($1)" % [a.res])
-  else:
-    r.res.add(a.res)
-  for i in countup(2, sonsLen(n) - 1):
-    gen(p, n.sons[i], a)
-    if skipTypes(n.sons[i].typ, abstractVarRange).kind == tyChar:
-      r.res.add(".chr($1)" % [a.res])
-    else:
-      r.res.add(".$1" % [a.res])
-
 proc genToArray(p: PProc; n: PNode; r: var TCompRes) =
   # we map mArray to PHP's array constructor, a mild hack:
   var a, b: TCompRes
@@ -1738,9 +1560,9 @@ proc genToArray(p: PProc; n: PNode; r: var TCompRes) =
         gen(p, it[1], b)
         r.res.add("$# => $#" % [a.rdLoc, b.rdLoc])
       else:
-        localError(it.info, "'toArray' needs tuple constructors")
+        localError(p.config, it.info, "'toArray' needs tuple constructors")
   else:
-    localError(x.info, "'toArray' needs an array literal")
+    localError(p.config, x.info, "'toArray' needs an array literal")
   r.res.add(")")
 
 proc genReprAux(p: PProc, n: PNode, r: var TCompRes, magic: string, typ: Rope = nil) =
@@ -1766,9 +1588,6 @@ proc genReprAux(p: PProc, n: PNode, r: var TCompRes, magic: string, typ: Rope =
   add(r.res, ")")
 
 proc genRepr(p: PProc, n: PNode, r: var TCompRes) =
-  if p.target == targetPHP:
-    localError(n.info, "'repr' not available for PHP backend")
-    return
   let t = skipTypes(n.sons[1].typ, abstractVarRange)
   case t.kind:
   of tyInt..tyInt64, tyUInt..tyUInt64:
@@ -1786,7 +1605,7 @@ proc genRepr(p: PProc, n: PNode, r: var TCompRes) =
   of tySet:
     genReprAux(p, n, r, "reprSet", genTypeInfo(p, t))
   of tyEmpty, tyVoid:
-    localError(n.info, "'repr' doesn't support 'void' type")
+    localError(p.config, n.info, "'repr' doesn't support 'void' type")
   of tyPointer:
     genReprAux(p, n, r, "reprPointer")
   of tyOpenArray, tyVarargs:
@@ -1828,57 +1647,36 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
     if not (optOverflowCheck in p.options): unaryExpr(p, n, r, "", "$1 - 1")
     else: unaryExpr(p, n, r, "subInt", "subInt($1, 1)")
   of mAppendStrCh:
-    if p.target == targetJS:
-      binaryExpr(p, n, r, "addChar",
-          "if ($1 != null) { addChar($1, $2); } else { $1 = [$2, 0]; }")
-    else:
-      binaryExpr(p, n, r, "", "$1 .= chr($2)")
+    binaryExpr(p, n, r, "addChar",
+        "if ($1 != null) { addChar($1, $2); } else { $1 = [$2, 0]; }")
   of mAppendStrStr:
-    if p.target == targetJS:
-      if skipTypes(n.sons[1].typ, abstractVarRange).kind == tyCString:
-          binaryExpr(p, n, r, "", "if ($1 != null) { $1 += $2; } else { $1 = $2; }")
-      else:
-        binaryExpr(p, n, r, "",
-          "if ($1 != null) { $1 = ($1.slice(0, -1)).concat($2); } else { $1 = $2;}")
-      # XXX: make a copy of $2, because of Javascript's sucking semantics
+    if skipTypes(n.sons[1].typ, abstractVarRange).kind == tyCString:
+        binaryExpr(p, n, r, "", "if ($1 != null) { $1 += $2; } else { $1 = $2; }")
     else:
-      binaryExpr(p, n, r, "", "$1 .= $2;")
+      binaryExpr(p, n, r, "",
+        "if ($1 != null) { $1 = ($1.slice(0, -1)).concat($2); } else { $1 = $2;}")
+    # XXX: make a copy of $2, because of Javascript's sucking semantics
   of mAppendSeqElem:
-    if p.target == targetJS:
-      var x, y: TCompRes
-      gen(p, n.sons[1], x)
-      gen(p, n.sons[2], y)
-      if needsNoCopy(p, n[2]):
-        r.res = "if ($1 != null) { $1.push($2); } else { $1 = [$2]; }" % [x.rdLoc, y.rdLoc]
-      else:
-        useMagic(p, "nimCopy")
-        let c = getTemp(p, defineInLocals=false)
-        lineF(p, "var $1 = nimCopy(null, $2, $3);$n",
-             [c, y.rdLoc, genTypeInfo(p, n[2].typ)])
-        r.res = "if ($1 != null) { $1.push($2); } else { $1 = [$2]; }" % [x.rdLoc, c]
-      r.kind = resExpr
+    var x, y: TCompRes
+    gen(p, n.sons[1], x)
+    gen(p, n.sons[2], y)
+    if needsNoCopy(p, n[2]):
+      r.res = "if ($1 != null) { $1.push($2); } else { $1 = [$2]; }" % [x.rdLoc, y.rdLoc]
     else:
-      binaryExpr(p, n, r, "", "$1[] = $2")
+      useMagic(p, "nimCopy")
+      let c = getTemp(p, defineInLocals=false)
+      lineF(p, "var $1 = nimCopy(null, $2, $3);$n",
+            [c, y.rdLoc, genTypeInfo(p, n[2].typ)])
+      r.res = "if ($1 != null) { $1.push($2); } else { $1 = [$2]; }" % [x.rdLoc, c]
+    r.kind = resExpr
   of mConStrStr:
-    if p.target == targetJS:
-      genConStrStr(p, n, r)
-    else:
-      genConStrStrPHP(p, n, r)
+    genConStrStr(p, n, r)
   of mEqStr:
-    if p.target == targetJS:
-      binaryExpr(p, n, r, "eqStrings", "eqStrings($1, $2)")
-    else:
-      binaryExpr(p, n, r, "", "($1 == $2)")
+    binaryExpr(p, n, r, "eqStrings", "eqStrings($1, $2)")
   of mLeStr:
-    if p.target == targetJS:
-      binaryExpr(p, n, r, "cmpStrings", "(cmpStrings($1, $2) <= 0)")
-    else:
-      binaryExpr(p, n, r, "", "($1 <= $2)")
+    binaryExpr(p, n, r, "cmpStrings", "(cmpStrings($1, $2) <= 0)")
   of mLtStr:
-    if p.target == targetJS:
-      binaryExpr(p, n, r, "cmpStrings", "(cmpStrings($1, $2) < 0)")
-    else:
-      binaryExpr(p, n, r, "", "($1 < $2)")
+    binaryExpr(p, n, r, "cmpStrings", "(cmpStrings($1, $2) < 0)")
   of mIsNil: unaryExpr(p, n, r, "", "($1 === null)")
   of mEnumToStr: genRepr(p, n, r)
   of mNew, mNewFinalize: genNew(p, n)
@@ -1886,24 +1684,20 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
   of mChr, mArrToSeq: gen(p, n.sons[1], r)      # nothing to do
   of mOrd: genOrd(p, n, r)
   of mLengthStr:
-    if p.target == targetJS and n.sons[1].typ.skipTypes(abstractInst).kind == tyCString:
+    if n.sons[1].typ.skipTypes(abstractInst).kind == tyCString:
       unaryExpr(p, n, r, "", "($1 != null ? $1.length : 0)")
     else:
-      unaryExpr(p, n, r, "", "($1 != null ? $1.length-1 : 0)" |
-                             "strlen($1)")
-  of mXLenStr: unaryExpr(p, n, r, "", "$1.length-1" | "strlen($1)")
+      unaryExpr(p, n, r, "", "($1 != null ? $1.length-1 : 0)")
+  of mXLenStr: unaryExpr(p, n, r, "", "$1.length-1")
   of mLengthSeq, mLengthOpenArray, mLengthArray:
-    unaryExpr(p, n, r, "", "($1 != null ? $1.length : 0)" |
-                           "count($1)")
+    unaryExpr(p, n, r, "", "($1 != null ? $1.length : 0)")
   of mXLenSeq:
-    unaryExpr(p, n, r, "", "$1.length" | "count($1)")
+    unaryExpr(p, n, r, "", "$1.length")
   of mHigh:
     if skipTypes(n.sons[1].typ, abstractVar).kind == tyString:
-      unaryExpr(p, n, r, "", "($1 != null ? ($1.length-2) : -1)" |
-                             "(strlen($1)-1)")
+      unaryExpr(p, n, r, "", "($1 != null ? ($1.length-2) : -1)")
     else:
-      unaryExpr(p, n, r, "", "($1 != null ? ($1.length-1) : -1)" |
-                             "(count($1)-1)")
+      unaryExpr(p, n, r, "", "($1 != null ? ($1.length-1) : -1)")
   of mInc:
     if n[1].typ.skipTypes(abstractRange).kind in tyUInt .. tyUInt64:
       binaryUintExpr(p, n, r, "+", true)
@@ -1917,7 +1711,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
       if optOverflowCheck notin p.options: binaryExpr(p, n, r, "", "$1 -= $2")
       else: binaryExpr(p, n, r, "subInt", "$1 = subInt($1, $2)")
   of mSetLengthStr:
-    binaryExpr(p, n, r, "", "$1.length = $2+1; $1[$1.length-1] = 0" | "$1 = substr($1, 0, $2)")
+    binaryExpr(p, n, r, "", "$1.length = $2+1; $1[$1.length-1] = 0")
   of mSetLengthSeq:
     var x, y: TCompRes
     gen(p, n.sons[1], x)
@@ -1934,50 +1728,23 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
   of mPlusSet: binaryExpr(p, n, r, "SetPlus", "SetPlus($1, $2)")
   of mMinusSet: binaryExpr(p, n, r, "SetMinus", "SetMinus($1, $2)")
   of mIncl: binaryExpr(p, n, r, "", "$1[$2] = true")
-  of mExcl: binaryExpr(p, n, r, "", "delete $1[$2]" | "unset $1[$2]")
+  of mExcl: binaryExpr(p, n, r, "", "delete $1[$2]")
   of mInSet:
-    if p.target == targetJS:
-      binaryExpr(p, n, r, "", "($1[$2] != undefined)")
-    else:
-      let s = n.sons[1]
-      if s.kind == nkCurly:
-        var a, b, x: TCompRes
-        gen(p, n.sons[2], x)
-        r.res = rope("(")
-        r.kind = resExpr
-        for i in countup(0, sonsLen(s) - 1):
-          if i > 0: add(r.res, " || ")
-          var it = s.sons[i]
-          if it.kind == nkRange:
-            gen(p, it.sons[0], a)
-            gen(p, it.sons[1], b)
-            addf(r.res, "($1 >= $2 && $1 <= $3)", [x.res, a.res, b.res,])
-          else:
-            gen(p, it, a)
-            addf(r.res, "($1 == $2)", [x.res, a.res])
-        add(r.res, ")")
-      else:
-        binaryExpr(p, n, r, "", "isset($1[$2])")
+    binaryExpr(p, n, r, "", "($1[$2] != undefined)")
   of mNewSeq: genNewSeq(p, n)
-  of mNewSeqOfCap: unaryExpr(p, n, r, "", "[]" | "array()")
+  of mNewSeqOfCap: unaryExpr(p, n, r, "", "[]")
   of mOf: genOf(p, n, r)
   of mReset: genReset(p, n)
   of mEcho: genEcho(p, n, r)
   of mNLen..mNError, mSlurp, mStaticExec:
-    localError(n.info, errXMustBeCompileTime, n.sons[0].sym.name.s)
+    localError(p.config, n.info, errXMustBeCompileTime % n.sons[0].sym.name.s)
   of mCopyStr:
-    binaryExpr(p, n, r, "", "($1.slice($2))" | "substr($1, $2)")
+    binaryExpr(p, n, r, "", "($1.slice($2))")
   of mCopyStrLast:
-    if p.target == targetJS:
-      ternaryExpr(p, n, r, "", "($1.slice($2, ($3)+1).concat(0))")
-    else:
-      ternaryExpr(p, n, r, "nimSubstr", "nimSubstr($#, $#, $#)")
+    ternaryExpr(p, n, r, "", "($1.slice($2, ($3)+1).concat(0))")
   of mNewString: unaryExpr(p, n, r, "mnewString", "mnewString($1)")
   of mNewStringOfCap:
-    if p.target == targetJS:
-      unaryExpr(p, n, r, "mnewString", "mnewString(0)")
-    else:
-      unaryExpr(p, n, r, "", "''")
+    unaryExpr(p, n, r, "mnewString", "mnewString(0)")
   of mDotDot:
     genProcForSymIfNeeded(p, n.sons[0].sym)
     genCall(p, n, r)
@@ -1985,11 +1752,10 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
     useMagic(p, "nimParseBiggestFloat")
     genCall(p, n, r)
   of mArray:
-    if p.target == targetPHP: genToArray(p, n, r)
-    else: genCall(p, n, r)
+    genCall(p, n, r)
   else:
     genCall(p, n, r)
-    #else internalError(e.info, 'genMagic: ' + magicToStr[op]);
+    #else internalError(p.config, e.info, 'genMagic: ' + magicToStr[op]);
 
 proc genSetConstr(p: PProc, n: PNode, r: var TCompRes) =
   var
@@ -2003,13 +1769,13 @@ proc genSetConstr(p: PProc, n: PNode, r: var TCompRes) =
     if it.kind == nkRange:
       gen(p, it.sons[0], a)
       gen(p, it.sons[1], b)
-      addf(r.res, "[$1, $2]" | "array($#,$#)", [a.res, b.res])
+      addf(r.res, "[$1, $2]", [a.res, b.res])
     else:
       gen(p, it, a)
       add(r.res, a.res)
   add(r.res, ")")
   # emit better code for constant sets:
-  if p.target == targetJS and isDeepConstExpr(n):
+  if isDeepConstExpr(n):
     inc(p.g.unique)
     let tmp = rope("ConstSet") & rope(p.g.unique)
     addf(p.g.constants, "var $1 = $2;$n", [tmp, r.res])
@@ -2017,25 +1783,25 @@ proc genSetConstr(p: PProc, n: PNode, r: var TCompRes) =
 
 proc genArrayConstr(p: PProc, n: PNode, r: var TCompRes) =
   var a: TCompRes
-  r.res = rope("[" | "array(")
+  r.res = rope("[")
   r.kind = resExpr
   for i in countup(0, sonsLen(n) - 1):
     if i > 0: add(r.res, ", ")
     gen(p, n.sons[i], a)
     add(r.res, a.res)
-  add(r.res, "]" | ")")
+  add(r.res, "]")
 
 proc genTupleConstr(p: PProc, n: PNode, r: var TCompRes) =
   var a: TCompRes
-  r.res = rope("{" | "array(")
+  r.res = rope("{")
   r.kind = resExpr
   for i in countup(0, sonsLen(n) - 1):
     if i > 0: add(r.res, ", ")
     var it = n.sons[i]
     if it.kind == nkExprColonExpr: it = it.sons[1]
     gen(p, it, a)
-    addf(r.res, "Field$#: $#" | "$2", [i.rope, a.res])
-  r.res.add("}" | ")")
+    addf(r.res, "Field$#: $#", [i.rope, a.res])
+  r.res.add("}")
 
 proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) =
   var a: TCompRes
@@ -2045,7 +1811,7 @@ proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) =
   for i in countup(1, sonsLen(n) - 1):
     if i > 1: add(initList, ", ")
     var it = n.sons[i]
-    internalAssert it.kind == nkExprColonExpr
+    internalAssert p.config, it.kind == nkExprColonExpr
     let val = it.sons[1]
     gen(p, val, a)
     var f = it.sons[0].sym
@@ -2059,10 +1825,10 @@ proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) =
     else:
       useMagic(p, "nimCopy")
       a.res = "nimCopy(null, $1, $2)" % [a.rdLoc, genTypeInfo(p, typ)]
-    addf(initList, "$#: $#" | "'$#' => $#" , [f.loc.r, a.res])
+    addf(initList, "$#: $#", [f.loc.r, a.res])
   let t = skipTypes(n.typ, abstractInst + skipPtrs)
   createObjInitList(p, t, fieldIDs, initList)
-  r.res = ("{$1}" | "array($#)") % [initList]
+  r.res = ("{$1}") % [initList]
 
 proc genConv(p: PProc, n: PNode, r: var TCompRes) =
   var dest = skipTypes(n.typ, abstractVarRange)
@@ -2101,7 +1867,7 @@ proc convStrToCStr(p: PProc, n: PNode, r: var TCompRes) =
     gen(p, n.sons[0].sons[0], r)
   else:
     gen(p, n.sons[0], r)
-    if r.res == nil: internalError(n.info, "convStrToCStr")
+    if r.res == nil: internalError(p.config, n.info, "convStrToCStr")
     useMagic(p, "toJSStr")
     r.res = "toJSStr($1)" % [r.res]
     r.kind = resExpr
@@ -2113,30 +1879,29 @@ proc convCStrToStr(p: PProc, n: PNode, r: var TCompRes) =
     gen(p, n.sons[0].sons[0], r)
   else:
     gen(p, n.sons[0], r)
-    if r.res == nil: internalError(n.info, "convCStrToStr")
+    if r.res == nil: internalError(p.config, n.info, "convCStrToStr")
     useMagic(p, "cstrToNimstr")
     r.res = "cstrToNimstr($1)" % [r.res]
     r.kind = resExpr
 
 proc genReturnStmt(p: PProc, n: PNode) =
-  if p.procDef == nil: internalError(n.info, "genReturnStmt")
+  if p.procDef == nil: internalError(p.config, n.info, "genReturnStmt")
   p.beforeRetNeeded = true
   if n.sons[0].kind != nkEmpty:
     genStmt(p, n.sons[0])
   else:
     genLineDir(p, n)
-  lineF(p, "break BeforeRet;$n" | "goto BeforeRet;$n", [])
+  lineF(p, "break BeforeRet;$n", [])
 
 proc frameCreate(p: PProc; procname, filename: Rope): Rope =
   let frameFmt =
-    "var F={procname:$1,prev:framePtr,filename:$2,line:0};$n" |
-    "global $$framePtr; $$F=array('procname'=>$#,'prev'=>$$framePtr,'filename'=>$#,'line'=>0);$n"
+    "var F={procname:$1,prev:framePtr,filename:$2,line:0};$n"
 
   result = p.indentLine(frameFmt % [procname, filename])
-  result.add p.indentLine(ropes.`%`("framePtr = F;$n" | "$$framePtr = &$$F;$n", []))
+  result.add p.indentLine(ropes.`%`("framePtr = F;$n", []))
 
 proc frameDestroy(p: PProc): Rope =
-  result = p.indentLine rope(("framePtr = F.prev;" | "$framePtr = $F['prev'];") & tnl)
+  result = p.indentLine rope(("framePtr = F.prev;") & tnl)
 
 proc genProcBody(p: PProc, prc: PSym): Rope =
   if hasFrameInfo(p):
@@ -2146,15 +1911,12 @@ proc genProcBody(p: PProc, prc: PSym): Rope =
   else:
     result = nil
   if p.beforeRetNeeded:
-    if p.target == targetJS:
-      result.add p.indentLine(~"BeforeRet: do {$n")
-      result.add p.body
-      result.add p.indentLine(~"} while (false);$n")
-    else:
-      addF(result, "$# BeforeRet:;$n", [p.body])
+    result.add p.indentLine(~"BeforeRet: do {$n")
+    result.add p.body
+    result.add p.indentLine(~"} while (false);$n")
   else:
     add(result, p.body)
-  if prc.typ.callConv == ccSysCall and p.target == targetJS:
+  if prc.typ.callConv == ccSysCall:
     result = ("try {$n$1} catch (e) {$n" &
       " alert(\"Unhandled exception:\\n\" + e.message + \"\\n\"$n}") % [result]
   if hasFrameInfo(p):
@@ -2182,7 +1944,7 @@ proc genProc(oldProc: PProc, prc: PSym): Rope =
     resultSym = prc.ast.sons[resultPos].sym
     let mname = mangleName(p.module, resultSym)
     let resVar = createVar(p, resultSym.typ, isIndirect(resultSym))
-    resultAsgn = p.indentLine(("var $# = $#;$n" | "$$$# = $#;$n") % [mname, resVar])
+    resultAsgn = p.indentLine(("var $# = $#;$n") % [mname, resVar])
     if resultSym.typ.kind in {tyVar, tyPtr, tyLent, tyRef} and
         mapType(p, resultSym.typ) == etyBaseIndex:
       resultAsgn.add p.indentLine("var $#_Idx = 0;$n" % [mname])
@@ -2208,7 +1970,7 @@ proc genProc(oldProc: PProc, prc: PSym): Rope =
   else:
     result = ~tnl
 
-    if optHotCodeReloading in gOptions:
+    if optHotCodeReloading in p.config.options:
       # Here, we introduce thunks that create the equivalent of a jump table
       # for all global functions, because references to them may be stored
       # in JavaScript variables. The added indirection ensures that such
@@ -2267,8 +2029,7 @@ proc genCast(p: PProc, n: PNode, r: var TCompRes) =
     elif fromUint:
       if src.size == 4 and dest.size == 4:
         # XXX prevent multi evaluations
-        r.res = "($1|0)" % [r.res] |
-          "($1>(float)2147483647?(int)$1-4294967296:$1)" % [r.res]
+        r.res = "($1|0)" % [r.res]
       else:
         let trimmer = unsignedTrimmer(dest.size)
         let minuend = case dest.size
@@ -2304,8 +2065,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
       r.res = rope"null"
       r.kind = resExpr
   of nkStrLit..nkTripleStrLit:
-    if skipTypes(n.typ, abstractVarRange).kind == tyString and
-       p.target == targetJS:
+    if skipTypes(n.typ, abstractVarRange).kind == tyString:
       useMagic(p, "makeNimstrLit")
       r.res = "makeNimstrLit($1)" % [makeJSString(n.strVal)]
     else:
@@ -2342,10 +2102,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
   of nkObjConstr: genObjConstr(p, n, r)
   of nkHiddenStdConv, nkHiddenSubConv, nkConv: genConv(p, n, r)
   of nkAddr, nkHiddenAddr:
-    if p.target == targetJS:
-      genAddr(p, n, r)
-    else:
-      gen(p, n.sons[0], r)
+    genAddr(p, n, r)
   of nkDerefExpr, nkHiddenDeref: genDeref(p, n, r)
   of nkBracketExpr: genArrayAccess(p, n, r)
   of nkDotExpr: genFieldAccess(p, n, r)
@@ -2384,7 +2141,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
   of nkVarSection, nkLetSection: genVarStmt(p, n)
   of nkConstSection: discard
   of nkForStmt, nkParForStmt:
-    internalError(n.info, "for statement not eliminated")
+    internalError(p.config, n.info, "for statement not eliminated")
   of nkCaseStmt: genCaseJS(p, n, r)
   of nkReturnStmt: genReturnStmt(p, n)
   of nkBreakStmt: genBreakStmt(p, n)
@@ -2407,13 +2164,13 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
       genSym(p, n.sons[namePos], r)
       r.res = nil
   of nkGotoState, nkState:
-    internalError(n.info, "first class iterators not implemented")
+    internalError(p.config, n.info, "first class iterators not implemented")
   of nkPragmaBlock: gen(p, n.lastSon, r)
   of nkComesFrom:
     discard "XXX to implement for better stack traces"
-  else: internalError(n.info, "gen: unknown node type: " & $n.kind)
+  else: internalError(p.config, n.info, "gen: unknown node type: " & $n.kind)
 
-var globals: PGlobals
+var globals: PGlobals # XXX global variable here
 
 proc newModule(module: PSym): BModule =
   new(result)
@@ -2422,31 +2179,22 @@ proc newModule(module: PSym): BModule =
   if globals == nil:
     globals = newGlobals()
 
-proc genHeader(target: TTarget): Rope =
-  if target == targetJS:
-    result = (
-      "/* Generated by the Nim Compiler v$1 */$n" &
-      "/*   (c) " & copyrightYear & " Andreas Rumpf */$n$n" &
-      "var framePtr = null;$n" &
-      "var excHandler = 0;$n" &
-      "var lastJSError = null;$n" &
-      "if (typeof Int8Array === 'undefined') Int8Array = Array;$n" &
-      "if (typeof Int16Array === 'undefined') Int16Array = Array;$n" &
-      "if (typeof Int32Array === 'undefined') Int32Array = Array;$n" &
-      "if (typeof Uint8Array === 'undefined') Uint8Array = Array;$n" &
-      "if (typeof Uint16Array === 'undefined') Uint16Array = Array;$n" &
-      "if (typeof Uint32Array === 'undefined') Uint32Array = Array;$n" &
-      "if (typeof Float32Array === 'undefined') Float32Array = Array;$n" &
-      "if (typeof Float64Array === 'undefined') Float64Array = Array;$n") %
-      [rope(VersionAsString)]
-  else:
-    result = ("<?php$n" &
-              "/* Generated by the Nim Compiler v$1 */$n" &
-              "/*   (c) " & copyrightYear & " Andreas Rumpf */$n$n" &
-              "$$framePtr = null;$n" &
-              "$$excHandler = 0;$n" &
-              "$$lastJSError = null;$n") %
-             [rope(VersionAsString)]
+proc genHeader(): Rope =
+  result = (
+    "/* Generated by the Nim Compiler v$1 */$n" &
+    "/*   (c) " & copyrightYear & " Andreas Rumpf */$n$n" &
+    "var framePtr = null;$n" &
+    "var excHandler = 0;$n" &
+    "var lastJSError = null;$n" &
+    "if (typeof Int8Array === 'undefined') Int8Array = Array;$n" &
+    "if (typeof Int16Array === 'undefined') Int16Array = Array;$n" &
+    "if (typeof Int32Array === 'undefined') Int32Array = Array;$n" &
+    "if (typeof Uint8Array === 'undefined') Uint8Array = Array;$n" &
+    "if (typeof Uint16Array === 'undefined') Uint16Array = Array;$n" &
+    "if (typeof Uint32Array === 'undefined') Uint32Array = Array;$n" &
+    "if (typeof Float32Array === 'undefined') Float32Array = Array;$n" &
+    "if (typeof Float64Array === 'undefined') Float64Array = Array;$n") %
+    [rope(VersionAsString)]
 
 proc genModule(p: PProc, n: PNode) =
   if optStackTrace in p.options:
@@ -2458,10 +2206,10 @@ proc genModule(p: PProc, n: PNode) =
     add(p.body, frameDestroy(p))
 
 proc myProcess(b: PPassContext, n: PNode): PNode =
-  if passes.skipCodegen(n): return n
   result = n
-  var m = BModule(b)
-  if m.module == nil: internalError(n.info, "myProcess")
+  let m = BModule(b)
+  if passes.skipCodegen(m.config, n): return n
+  if m.module == nil: internalError(m.config, n.info, "myProcess")
   var p = newProc(globals, m, nil, m.module.options)
   p.unique = globals.unique
   genModule(p, n)
@@ -2488,11 +2236,11 @@ proc getClassName(t: PType): Rope =
   if s.isNil or sfAnon in s.flags:
     s = skipTypes(t, abstractPtrs).sym
   if s.isNil or sfAnon in s.flags:
-    internalError("cannot retrieve class name")
+    doAssert(false, "cannot retrieve class name")
   if s.loc.r != nil: result = s.loc.r
   else: result = rope(s.name.s)
 
-proc genClass(obj: PType; content: Rope; ext: string) =
+proc genClass(conf: ConfigRef; obj: PType; content: Rope; ext: string) =
   let cls = getClassName(obj)
   let t = skipTypes(obj, abstractPtrs)
   let extends = if t.kind == tyObject and t.sons[0] != nil:
@@ -2505,35 +2253,36 @@ proc genClass(obj: PType; content: Rope; ext: string) =
             "class $#$# {$n$#$n}$n") %
            [rope(VersionAsString), cls, extends, content]
 
-  let outfile = changeFileExt(completeCFilePath($cls), ext)
+  let outfile = changeFileExt(completeCFilePath(conf, $cls), ext)
   discard writeRopeIfNotEqual(result, outfile)
 
 proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode =
-  if passes.skipCodegen(n): return n
   result = myProcess(b, n)
   var m = BModule(b)
+  if passes.skipCodegen(m.config, n): return n
   if sfMainModule in m.module.flags:
-    let ext = if m.target == targetJS: "js" else: "php"
-    let f = if globals.classes.len == 0: m.module.filename
+    let ext = "js"
+    let f = if globals.classes.len == 0: toFilename(FileIndex m.module.position)
             else: "nimsystem"
     let code = wholeCode(graph, m)
     let outfile =
-      if options.outFile.len > 0:
-        if options.outFile.isAbsolute: options.outFile
-        else: getCurrentDir() / options.outFile
+      if m.config.outFile.len > 0:
+        if m.config.outFile.isAbsolute: m.config.outFile
+        else: getCurrentDir() / m.config.outFile
       else:
-        changeFileExt(completeCFilePath(f), ext)
-    discard writeRopeIfNotEqual(genHeader(m.target) & code, outfile)
+        changeFileExt(completeCFilePath(m.config, f), ext)
+    discard writeRopeIfNotEqual(genHeader() & code, outfile)
     for obj, content in items(globals.classes):
-      genClass(obj, content, ext)
+      genClass(m.config, obj, content, ext)
 
 proc myOpenCached(graph: ModuleGraph; s: PSym, rd: PRodReader): PPassContext =
-  internalError("symbol files are not possible with the JS code generator")
+  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.target = if gCmd == cmdCompileToPHP: targetPHP else: targetJS
+  r.graph = graph
+  r.config = graph.config
   result = r
 
 const JSgenPass* = makePass(myOpen, myOpenCached, myProcess, myClose)
diff --git a/compiler/jstypes.nim b/compiler/jstypes.nim
index 8bd963a65..f9e4246eb 100644
--- a/compiler/jstypes.nim
+++ b/compiler/jstypes.nim
@@ -38,7 +38,7 @@ proc genObjectFields(p: PProc, typ: PType, n: PNode): Rope =
                     makeJSString(field.name.s)]
   of nkRecCase:
     length = sonsLen(n)
-    if (n.sons[0].kind != nkSym): internalError(n.info, "genObjectFields")
+    if (n.sons[0].kind != nkSym): internalError(p.config, n.info, "genObjectFields")
     field = n.sons[0].sym
     s = genTypeInfo(p, field.typ)
     for i in countup(1, length - 1):
@@ -47,7 +47,7 @@ proc genObjectFields(p: PProc, typ: PType, n: PNode): Rope =
       case b.kind
       of nkOfBranch:
         if sonsLen(b) < 2:
-          internalError(b.info, "genObjectFields; nkOfBranch broken")
+          internalError(p.config, b.info, "genObjectFields; nkOfBranch broken")
         for j in countup(0, sonsLen(b) - 2):
           if u != nil: add(u, ", ")
           if b.sons[j].kind == nkRange:
@@ -57,7 +57,7 @@ proc genObjectFields(p: PProc, typ: PType, n: PNode): Rope =
             add(u, rope(getOrdValue(b.sons[j])))
       of nkElse:
         u = rope(lengthOrd(field.typ))
-      else: internalError(n.info, "genObjectFields(nkRecCase)")
+      else: internalError(p.config, n.info, "genObjectFields(nkRecCase)")
       if result != nil: add(result, ", " & tnl)
       addf(result, "[setConstr($1), $2]",
            [u, genObjectFields(p, typ, lastSon(b))])
@@ -65,7 +65,7 @@ proc genObjectFields(p: PProc, typ: PType, n: PNode): Rope =
         "typ: $2, name: $4, sons: [$5]}") % [
         mangleName(p.module, field), s,
         rope(lengthOrd(field.typ)), makeJSString(field.name.s), result]
-  else: internalError(n.info, "genObjectFields")
+  else: internalError(p.config, n.info, "genObjectFields")
 
 proc objHasTypeField(t: PType): bool {.inline.} =
   tfInheritable in t.flags or t.sons[0] != nil
@@ -104,7 +104,7 @@ proc genEnumInfo(p: PProc, typ: PType, name: Rope) =
   let length = sonsLen(typ.n)
   var s: Rope = nil
   for i in countup(0, length - 1):
-    if (typ.n.sons[i].kind != nkSym): internalError(typ.n.info, "genEnumInfo")
+    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)
     let extName = if field.ast == nil: field.name.s else: field.ast.strVal
@@ -121,26 +121,7 @@ proc genEnumInfo(p: PProc, typ: PType, name: Rope) =
     addf(p.g.typeInfo, "$1.base = $2;$n",
          [name, genTypeInfo(p, typ.sons[0])])
 
-proc genEnumInfoPHP(p: PProc; t: PType): Rope =
-  let t = t.skipTypes({tyGenericInst, tyDistinct, tyAlias, tySink})
-  result = "$$NTI$1" % [rope(t.id)]
-  p.declareGlobal(t.id, result)
-  if containsOrIncl(p.g.typeInfoGenerated, t.id): return
-
-  let length = sonsLen(t.n)
-  var s: Rope = nil
-  for i in countup(0, length - 1):
-    if (t.n.sons[i].kind != nkSym): internalError(t.n.info, "genEnumInfo")
-    let field = t.n.sons[i].sym
-    if i > 0: add(s, ", " & tnl)
-    let extName = if field.ast == nil: field.name.s else: field.ast.strVal
-    addf(s, "$# => $#$n",
-         [rope(field.position), makeJSString(extName)])
-  prepend(p.g.typeInfo, "$$$# = $#;$n" % [result, s])
-
 proc genTypeInfo(p: PProc, typ: PType): Rope =
-  if p.target == targetPHP:
-    return makeJSString(typeToString(typ, preferModuleInfo))
   let t = typ.skipTypes({tyGenericInst, tyDistinct, tyAlias, tySink})
   result = "NTI$1" % [rope(t.id)]
   if containsOrIncl(p.g.typeInfoGenerated, t.id): return
@@ -171,5 +152,5 @@ proc genTypeInfo(p: PProc, typ: PType): Rope =
   of tyTuple: genTupleInfo(p, t, result)
   of tyStatic:
     if t.n != nil: result = genTypeInfo(p, lastSon t)
-    else: internalError("genTypeInfo(" & $t.kind & ')')
-  else: internalError("genTypeInfo(" & $t.kind & ')')
+    else: internalError(p.config, "genTypeInfo(" & $t.kind & ')')
+  else: internalError(p.config, "genTypeInfo(" & $t.kind & ')')
diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim
index d881df5e9..773e5e29c 100644
--- a/compiler/lambdalifting.nim
+++ b/compiler/lambdalifting.nim
@@ -11,7 +11,8 @@
 
 import
   intsets, strutils, options, ast, astalgo, trees, treetab, msgs, os,
-  idents, renderer, types, magicsys, rodread, lowerings, tables
+  idents, renderer, types, magicsys, rodread, lowerings, tables,
+  modulegraphs
 
 discard """
   The basic approach is that captured vars need to be put on the heap and
@@ -125,32 +126,32 @@ proc newCall(a: PSym, b: PNode): PNode =
   result.add newSymNode(a)
   result.add b
 
-proc createStateType(iter: PSym): PType =
+proc createStateType(g: ModuleGraph; iter: PSym): PType =
   var n = newNodeI(nkRange, iter.info)
   addSon(n, newIntNode(nkIntLit, -1))
   addSon(n, newIntNode(nkIntLit, 0))
   result = newType(tyRange, iter)
   result.n = n
-  var intType = nilOrSysInt()
+  var intType = nilOrSysInt(g)
   if intType.isNil: intType = newType(tyInt, iter)
   rawAddSon(result, intType)
 
-proc createStateField(iter: PSym): PSym =
-  result = newSym(skField, getIdent(":state"), iter, iter.info)
-  result.typ = createStateType(iter)
+proc createStateField(g: ModuleGraph; iter: PSym): PSym =
+  result = newSym(skField, getIdent(":state"), iter, iter.info, {})
+  result.typ = createStateType(g, iter)
 
-proc createEnvObj(owner: PSym; info: TLineInfo): PType =
+proc createEnvObj(g: ModuleGraph; owner: PSym; info: TLineInfo): PType =
   # YYY meh, just add the state field for every closure for now, it's too
   # hard to figure out if it comes from a closure iterator:
-  result = createObj(owner, info, final=false)
-  rawAddField(result, createStateField(owner))
+  result = createObj(g, owner, info, final=false)
+  rawAddField(result, createStateField(g, owner))
 
 proc getIterResult(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":result", iter, iter.info, {})
     result.typ = iter.typ.sons[0]
     incl(result.flags, sfUsed)
     iter.ast.add newSymNode(result)
@@ -166,7 +167,7 @@ proc addHiddenParam(routine: PSym, param: PSym) =
   assert sfFromGeneric in param.flags
   #echo "produced environment: ", param.id, " for ", routine.id
 
-proc getHiddenParam(routine: PSym): PSym =
+proc getHiddenParam(g: ModuleGraph; routine: PSym): PSym =
   let params = routine.ast.sons[paramsPos]
   let hidden = lastSon(params)
   if hidden.kind == nkSym and hidden.sym.kind == skParam and hidden.sym.name.s == paramName:
@@ -174,7 +175,7 @@ proc getHiddenParam(routine: PSym): PSym =
     assert sfFromGeneric in result.flags
   else:
     # writeStackTrace()
-    localError(routine.info, "internal error: could not find env param for " & routine.name.s)
+    localError(g.config, routine.info, "internal error: could not find env param for " & routine.name.s)
     result = routine
 
 proc getEnvParam*(routine: PSym): PSym =
@@ -208,14 +209,14 @@ proc newAsgnStmt(le, ri: PNode, info: TLineInfo): PNode =
   result.sons[0] = le
   result.sons[1] = ri
 
-proc makeClosure*(prc: PSym; env: PNode; info: TLineInfo): PNode =
+proc makeClosure*(g: ModuleGraph; prc: PSym; env: PNode; info: TLineInfo): PNode =
   result = newNodeIT(nkClosure, info, prc.typ)
   result.add(newSymNode(prc))
   if env == nil:
-    result.add(newNodeIT(nkNilLit, info, getSysType(tyNil)))
+    result.add(newNodeIT(nkNilLit, info, getSysType(g, info, tyNil)))
   else:
     if env.skipConv.kind == nkClosure:
-      localError(info, "internal error: taking closure of closure")
+      localError(g.config, info, "internal error: taking closure of closure")
     result.add(env)
 
 proc interestingIterVar(s: PSym): bool {.inline.} =
@@ -227,23 +228,23 @@ proc interestingIterVar(s: PSym): bool {.inline.} =
 template isIterator*(owner: PSym): bool =
   owner.kind == skIterator and owner.typ.callConv == ccClosure
 
-proc liftingHarmful(owner: PSym): bool {.inline.} =
+proc liftingHarmful(conf: ConfigRef; owner: PSym): bool {.inline.} =
   ## lambda lifting can be harmful for JS-like code generators.
   let isCompileTime = sfCompileTime in owner.flags or owner.kind == skMacro
-  result = gCmd in {cmdCompileToPHP, cmdCompileToJS} and not isCompileTime
+  result = conf.cmd == cmdCompileToJS and not isCompileTime
 
-proc liftIterSym*(n: PNode; owner: PSym): PNode =
+proc liftIterSym*(g: ModuleGraph; n: PNode; owner: PSym): PNode =
   # transforms  (iter)  to  (let env = newClosure[iter](); (iter, env))
-  if liftingHarmful(owner): return n
+  if liftingHarmful(g.config, owner): return n
   let iter = n.sym
   assert iter.isIterator
 
   result = newNodeIT(nkStmtListExpr, n.info, n.typ)
 
-  let hp = getHiddenParam(iter)
+  let hp = getHiddenParam(g, iter)
   var env: PNode
   if owner.isIterator:
-    let it = getHiddenParam(owner)
+    let it = getHiddenParam(g, owner)
     addUniqueField(it.typ.sons[0], hp)
     env = indirectAccess(newSymNode(it), hp, hp.info)
   else:
@@ -255,11 +256,11 @@ proc liftIterSym*(n: PNode; owner: PSym): PNode =
     addVar(v, env)
     result.add(v)
   # add 'new' statement:
-  result.add newCall(getSysSym"internalNew", env)
-  result.add makeClosure(iter, env, n.info)
+  result.add newCall(getSysSym(g, n.info, "internalNew"), env)
+  result.add makeClosure(g, iter, env, n.info)
 
-proc freshVarForClosureIter*(s, owner: PSym): PNode =
-  let envParam = getHiddenParam(owner)
+proc freshVarForClosureIter*(g: ModuleGraph; s, owner: PSym): PNode =
+  let envParam = getHiddenParam(g, owner)
   let obj = envParam.typ.lastSon
   addField(obj, s)
 
@@ -269,18 +270,18 @@ proc freshVarForClosureIter*(s, owner: PSym): PNode =
   if field != nil:
     result = rawIndirectAccess(access, field, s.info)
   else:
-    localError(s.info, "internal error: cannot generate fresh variable")
+    localError(g.config, s.info, "internal error: cannot generate fresh variable")
     result = access
 
 # ------------------ new stuff -------------------------------------------
 
-proc markAsClosure(owner: PSym; n: PNode) =
+proc markAsClosure(g: ModuleGraph; owner: PSym; n: PNode) =
   let s = n.sym
   if illegalCapture(s):
-    localError(n.info, "illegal capture '$1' of type <$2> which is declared here: $3" %
+    localError(g.config, n.info, "illegal capture '$1' of type <$2> which is declared here: $3" %
       [s.name.s, typeToString(s.typ), $s.info])
   elif owner.typ.callConv notin {ccClosure, ccDefault}:
-    localError(n.info, "illegal capture '$1' because '$2' has the calling convention: <$3>" %
+    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]])
   incl(owner.typ.flags, tfCapturesEnv)
   owner.typ.callConv = ccClosure
@@ -290,12 +291,14 @@ type
     processed, capturedVars: IntSet
     ownerToType: Table[int, PType]
     somethingToDo: bool
+    graph: ModuleGraph
 
-proc initDetectionPass(fn: PSym): DetectionPass =
+proc initDetectionPass(g: ModuleGraph; fn: PSym): DetectionPass =
   result.processed = initIntSet()
   result.capturedVars = initIntSet()
   result.ownerToType = initTable[int, PType]()
   result.processed.incl(fn.id)
+  result.graph = g
 
 discard """
 proc outer =
@@ -312,7 +315,7 @@ proc getEnvTypeForOwner(c: var DetectionPass; owner: PSym;
   result = c.ownerToType.getOrDefault(owner.id)
   if result.isNil:
     result = newType(tyRef, owner)
-    let obj = createEnvObj(owner, info)
+    let obj = createEnvObj(c.graph, owner, info)
     rawAddSon(result, obj)
     c.ownerToType[owner.id] = result
 
@@ -321,13 +324,13 @@ proc createUpField(c: var DetectionPass; dest, dep: PSym; info: TLineInfo) =
   let obj = refObj.lastSon
   let fieldType = c.getEnvTypeForOwner(dep, info) #getHiddenParam(dep).typ
   if refObj == fieldType:
-    localError(dep.info, "internal error: invalid up reference computed")
+    localError(c.graph.config, dep.info, "internal error: invalid up reference computed")
 
   let upIdent = getIdent(upName)
   let upField = lookupInRecord(obj.n, upIdent)
   if upField != nil:
     if upField.typ != fieldType:
-      localError(dep.info, "internal error: up references do not agree")
+      localError(c.graph.config, dep.info, "internal error: up references do not agree")
   else:
     let result = newSym(skField, upIdent, obj.owner, obj.owner.info)
     result.typ = fieldType
@@ -369,7 +372,7 @@ proc addClosureParam(c: var DetectionPass; fn: PSym; info: TLineInfo) =
     cp.typ = t
     addHiddenParam(fn, cp)
   elif cp.typ != t and fn.kind != skIterator:
-    localError(fn.info, "internal error: inconsistent environment type")
+    localError(c.graph.config, fn.info, "internal error: inconsistent environment type")
   #echo "adding closure to ", fn.name.s
 
 proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
@@ -395,7 +398,7 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
         addClosureParam(c, owner, n.info)
         if interestingIterVar(s):
           if not c.capturedVars.containsOrIncl(s.id):
-            let obj = getHiddenParam(owner).typ.lastSon
+            let obj = getHiddenParam(c.graph, owner).typ.lastSon
             #let obj = c.getEnvTypeForOwner(s.owner).lastSon
             addField(obj, s)
       # but always return because the rest of the proc is only relevant when
@@ -415,7 +418,7 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
       """
       # mark 'owner' as taking a closure:
       c.somethingToDo = true
-      markAsClosure(owner, n)
+      markAsClosure(c.graph, owner, n)
       addClosureParam(c, owner, n.info)
       #echo "capturing ", n.info
       # variable 's' is actually captured:
@@ -440,7 +443,7 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
           """
           let up = w.skipGenericOwner
           #echo "up for ", w.name.s, " up ", up.name.s
-          markAsClosure(w, n)
+          markAsClosure(c.graph, w, n)
           addClosureParam(c, w, n.info) # , ow
           createUpField(c, w, up, n.info)
           w = up
@@ -467,10 +470,10 @@ proc initLiftingPass(fn: PSym): LiftingPass =
   result.processed.incl(fn.id)
   result.envVars = initTable[int, PNode]()
 
-proc accessViaEnvParam(n: PNode; owner: PSym): PNode =
+proc accessViaEnvParam(g: ModuleGraph; n: PNode; owner: PSym): PNode =
   let s = n.sym
   # Type based expression construction for simplicity:
-  let envParam = getHiddenParam(owner)
+  let envParam = getHiddenParam(g, owner)
   if not envParam.isNil:
     var access = newSymNode(envParam)
     while true:
@@ -482,7 +485,7 @@ proc accessViaEnvParam(n: PNode; owner: PSym): PNode =
       let upField = lookupInRecord(obj.n, getIdent(upName))
       if upField == nil: break
       access = rawIndirectAccess(access, upField, n.info)
-  localError(n.info, "internal error: environment misses: " & s.name.s)
+  localError(g.config, n.info, "internal error: environment misses: " & s.name.s)
   result = n
 
 proc newEnvVar(owner: PSym; typ: PType): PNode =
@@ -501,22 +504,22 @@ proc newEnvVar(owner: PSym; typ: PType): PNode =
 proc setupEnvVar(owner: PSym; d: DetectionPass;
                  c: var LiftingPass): PNode =
   if owner.isIterator:
-    return getHiddenParam(owner).newSymNode
+    return getHiddenParam(d.graph, owner).newSymNode
   result = c.envvars.getOrDefault(owner.id)
   if result.isNil:
     let envVarType = d.ownerToType.getOrDefault(owner.id)
     if envVarType.isNil:
-      localError owner.info, "internal error: could not determine closure type"
+      localError d.graph.config, owner.info, "internal error: could not determine closure type"
     result = newEnvVar(owner, envVarType)
     c.envVars[owner.id] = result
 
-proc getUpViaParam(owner: PSym): PNode =
-  let p = getHiddenParam(owner)
+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))
     if upField == nil:
-      localError(owner.info, "could not find up reference for closure iter")
+      localError(g.config, owner.info, "could not find up reference for closure iter")
     else:
       result = rawIndirectAccess(result, upField, p.info)
 
@@ -526,7 +529,7 @@ proc rawClosureCreation(owner: PSym;
 
   var env: PNode
   if owner.isIterator:
-    env = getHiddenParam(owner).newSymNode
+    env = getHiddenParam(d.graph, owner).newSymNode
   else:
     env = setupEnvVar(owner, d, c)
     if env.kind == nkSym:
@@ -534,7 +537,7 @@ proc rawClosureCreation(owner: PSym;
       addVar(v, env)
       result.add(v)
     # add 'new' statement:
-    result.add(newCall(getSysSym"internalNew", env))
+    result.add(newCall(getSysSym(d.graph, env.info, "internalNew"), env))
     # add assignment statements for captured parameters:
     for i in 1..<owner.typ.n.len:
       let local = owner.typ.n[i].sym
@@ -545,7 +548,7 @@ proc rawClosureCreation(owner: PSym;
 
   let upField = lookupInRecord(env.typ.lastSon.n, getIdent(upName))
   if upField != nil:
-    let up = getUpViaParam(owner)
+    let up = getUpViaParam(d.graph, owner)
     if up != nil and upField.typ == up.typ:
       result.add(newAsgnStmt(rawIndirectAccess(env, upField, env.info),
                  up, env.info))
@@ -553,7 +556,7 @@ proc rawClosureCreation(owner: PSym;
     #  result.add(newAsgnStmt(rawIndirectAccess(env, upField, env.info),
     #             oldenv, env.info))
     else:
-      localError(env.info, "internal error: cannot create up reference")
+      localError(d.graph.config, env.info, "internal error: cannot create up reference")
 
 proc closureCreationForIter(iter: PNode;
                             d: DetectionPass; c: var LiftingPass): PNode =
@@ -561,10 +564,10 @@ proc closureCreationForIter(iter: PNode;
   let owner = iter.sym.skipGenericOwner
   var v = newSym(skVar, getIdent(envName), owner, iter.info)
   incl(v.flags, sfShadowed)
-  v.typ = getHiddenParam(iter.sym).typ
+  v.typ = getHiddenParam(d.graph, iter.sym).typ
   var vnode: PNode
   if owner.isIterator:
-    let it = getHiddenParam(owner)
+    let it = getHiddenParam(d.graph, owner)
     addUniqueField(it.typ.sons[0], v)
     vnode = indirectAccess(newSymNode(it), v, v.info)
   else:
@@ -572,7 +575,7 @@ proc closureCreationForIter(iter: PNode;
     var vs = newNodeI(nkVarSection, iter.info)
     addVar(vs, vnode)
     result.add(vs)
-  result.add(newCall(getSysSym"internalNew", vnode))
+  result.add(newCall(getSysSym(d.graph, iter.info, "internalNew"), vnode))
 
   let upField = lookupInRecord(v.typ.lastSon.n, getIdent(upName))
   if upField != nil:
@@ -581,8 +584,8 @@ proc closureCreationForIter(iter: PNode;
       result.add(newAsgnStmt(rawIndirectAccess(vnode, upField, iter.info),
                  u, iter.info))
     else:
-      localError(iter.info, "internal error: cannot create up reference for iter")
-  result.add makeClosure(iter.sym, vnode, iter.info)
+      localError(d.graph.config, iter.info, "internal error: cannot create up reference for iter")
+  result.add makeClosure(d.graph, iter.sym, vnode, iter.info)
 
 proc accessViaEnvVar(n: PNode; owner: PSym; d: DetectionPass;
                      c: var LiftingPass): PNode =
@@ -592,11 +595,11 @@ proc accessViaEnvVar(n: PNode; owner: PSym; d: DetectionPass;
   if field != nil:
     result = rawIndirectAccess(access, field, n.info)
   else:
-    localError(n.info, "internal error: not part of closure object type")
+    localError(d.graph.config, n.info, "internal error: not part of closure object type")
     result = n
 
-proc getStateField(owner: PSym): PSym =
-  getHiddenParam(owner).typ.sons[0].n.sons[0].sym
+proc getStateField(g: ModuleGraph; owner: PSym): PSym =
+  getHiddenParam(g, owner).typ.sons[0].n.sons[0].sym
 
 proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
                       c: var LiftingPass): PNode
@@ -604,8 +607,8 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
 proc transformYield(n: PNode; owner: PSym; d: DetectionPass;
                     c: var LiftingPass): PNode =
   if c.inContainer > 0:
-    localError(n.info, "invalid control flow: 'yield' within a constructor")
-  let state = getStateField(owner)
+    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
@@ -615,7 +618,8 @@ proc transformYield(n: PNode; owner: PSym; d: DetectionPass;
   var stateAsgnStmt = newNodeI(nkAsgn, n.info)
   stateAsgnStmt.add(rawIndirectAccess(newSymNode(getEnvParam(owner)),
                     state, n.info))
-  stateAsgnStmt.add(newIntTypeNode(nkIntLit, stateNo, getSysType(tyInt)))
+  stateAsgnStmt.add(newIntTypeNode(nkIntLit, stateNo,
+                    getSysType(d.graph, n.info, tyInt)))
 
   var retStmt = newNodeI(nkReturnStmt, n.info)
   if n.sons[0].kind != nkEmpty:
@@ -628,7 +632,8 @@ proc transformYield(n: PNode; owner: PSym; d: DetectionPass;
     retStmt.add(emptyNode)
 
   var stateLabelStmt = newNodeI(nkState, n.info)
-  stateLabelStmt.add(newIntTypeNode(nkIntLit, stateNo, getSysType(tyInt)))
+  stateLabelStmt.add(newIntTypeNode(nkIntLit, stateNo,
+                     getSysType(d.graph, n.info, tyInt)))
 
   result = newNodeI(nkStmtList, n.info)
   result.add(stateAsgnStmt)
@@ -637,16 +642,16 @@ proc transformYield(n: PNode; owner: PSym; d: DetectionPass;
 
 proc transformReturn(n: PNode; owner: PSym; d: DetectionPass;
                      c: var LiftingPass): PNode =
-  let state = getStateField(owner)
+  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(tyInt)))
+  stateAsgnStmt.add(newIntTypeNode(nkIntLit, -1, getSysType(d.graph, n.info, tyInt)))
   result.add(stateAsgnStmt)
   result.add(n)
 
-proc wrapIterBody(n: PNode; owner: PSym): PNode =
+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
@@ -660,7 +665,7 @@ proc wrapIterBody(n: PNode; owner: PSym): PNode =
   let info = n.info
   result = newNodeI(nkStmtList, info)
   var gs = newNodeI(nkGotoState, info)
-  gs.add(rawIndirectAccess(newSymNode(owner.getHiddenParam), getStateField(owner), 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))
@@ -669,9 +674,9 @@ proc wrapIterBody(n: PNode; owner: PSym): PNode =
   result.add(n)
 
   var stateAsgnStmt = newNodeI(nkAsgn, info)
-  stateAsgnStmt.add(rawIndirectAccess(newSymNode(owner.getHiddenParam),
-                    getStateField(owner), info))
-  stateAsgnStmt.add(newIntTypeNode(nkIntLit, -1, getSysType(tyInt)))
+  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;
@@ -679,25 +684,25 @@ proc symToClosure(n: PNode; owner: PSym; d: DetectionPass;
   let s = n.sym
   if s == owner:
     # recursive calls go through (lambda, hiddenParam):
-    let available = getHiddenParam(owner)
-    result = makeClosure(s, available.newSymNode, n.info)
+    let available = getHiddenParam(d.graph, owner)
+    result = makeClosure(d.graph, s, available.newSymNode, n.info)
   elif s.isIterator:
     result = closureCreationForIter(n, d, c)
   elif s.skipGenericOwner == owner:
     # direct dependency, so use the outer's env variable:
-    result = makeClosure(s, setupEnvVar(owner, d, c), n.info)
+    result = makeClosure(d.graph, s, setupEnvVar(owner, d, c), n.info)
   else:
-    let available = getHiddenParam(owner)
-    let wanted = getHiddenParam(s).typ
+    let available = getHiddenParam(d.graph, owner)
+    let wanted = getHiddenParam(d.graph, s).typ
     # ugh: call through some other inner proc;
     var access = newSymNode(available)
     while true:
       if access.typ == wanted:
-        return makeClosure(s, access, n.info)
+        return makeClosure(d.graph, s, access, n.info)
       let obj = access.typ.sons[0]
       let upField = lookupInRecord(obj.n, getIdent(upName))
       if upField == nil:
-        localError(n.info, "internal error: no environment found")
+        localError(d.graph.config, n.info, "internal error: no environment found")
         return n
       access = rawIndirectAccess(access, upField, n.info)
 
@@ -713,7 +718,7 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
         #  echo renderTree(s.getBody, {renderIds})
         let oldInContainer = c.inContainer
         c.inContainer = 0
-        let body = wrapIterBody(liftCapturedVars(s.getBody, s, d, c), s)
+        let body = wrapIterBody(d.graph, liftCapturedVars(s.getBody, s, d, c), s)
         if c.envvars.getOrDefault(s.id).isNil:
           s.ast.sons[bodyPos] = body
         else:
@@ -723,9 +728,9 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
         result = symToClosure(n, owner, d, c)
     elif s.id in d.capturedVars:
       if s.owner != owner:
-        result = accessViaEnvParam(n, owner)
+        result = accessViaEnvParam(d.graph, n, owner)
       elif owner.isIterator and interestingIterVar(s):
-        result = accessViaEnvParam(n, owner)
+        result = accessViaEnvParam(d.graph, n, owner)
       else:
         result = accessViaEnvVar(n, owner, d, c)
   of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit, nkComesFrom,
@@ -791,8 +796,8 @@ proc semCaptureSym*(s, owner: PSym) =
     # since the analysis is not entirely correct, we don't set 'tfCapturesEnv'
     # here
 
-proc liftIterToProc*(fn: PSym; body: PNode; ptrType: PType): PNode =
-  var d = initDetectionPass(fn)
+proc liftIterToProc*(g: ModuleGraph; fn: PSym; body: PNode; ptrType: PType): PNode =
+  var d = initDetectionPass(g, fn)
   var c = initLiftingPass(fn)
   # pretend 'fn' is a closure iterator for the analysis:
   let oldKind = fn.kind
@@ -801,25 +806,25 @@ proc liftIterToProc*(fn: PSym; body: PNode; ptrType: PType): PNode =
   fn.typ.callConv = ccClosure
   d.ownerToType[fn.id] = ptrType
   detectCapturedVars(body, fn, d)
-  result = wrapIterBody(liftCapturedVars(body, fn, d, c), fn)
+  result = wrapIterBody(g, liftCapturedVars(body, fn, d, c), fn)
   fn.kind = oldKind
   fn.typ.callConv = oldCC
 
-proc liftLambdas*(fn: PSym, body: PNode; tooEarly: var bool): PNode =
-  # XXX gCmd == cmdCompileToJS does not suffice! The compiletime stuff needs
+proc liftLambdas*(g: ModuleGraph; fn: PSym, body: PNode; tooEarly: var bool): PNode =
+  # XXX conf.cmd == cmdCompileToJS does not suffice! The compiletime stuff needs
   # the transformation even when compiling to JS ...
 
   # However we can do lifting for the stuff which is *only* compiletime.
   let isCompileTime = sfCompileTime in fn.flags or fn.kind == skMacro
 
   if body.kind == nkEmpty or (
-      gCmd in {cmdCompileToPHP, cmdCompileToJS} and not isCompileTime) or
+      g.config.cmd == cmdCompileToJS and not isCompileTime) or
       fn.skipGenericOwner.kind != skModule:
     # ignore forward declaration:
     result = body
     tooEarly = true
   else:
-    var d = initDetectionPass(fn)
+    var d = initDetectionPass(g, fn)
     detectCapturedVars(body, fn, d)
     if not d.somethingToDo and fn.isIterator:
       addClosureParam(d, fn, body.info)
@@ -829,7 +834,7 @@ proc liftLambdas*(fn: PSym, body: PNode; tooEarly: var bool): PNode =
       var newBody = liftCapturedVars(body, fn, d, c)
       if c.envvars.getOrDefault(fn.id) != nil:
         newBody = newTree(nkStmtList, rawClosureCreation(fn, d, c), newBody)
-      result = wrapIterBody(newBody, fn)
+      result = wrapIterBody(g, newBody, fn)
     else:
       result = body
     #if fn.name.s == "get2":
@@ -837,15 +842,12 @@ proc liftLambdas*(fn: PSym, body: PNode; tooEarly: var bool): PNode =
     #  echo renderTree(result, {renderIds})
 
 proc liftLambdasForTopLevel*(module: PSym, body: PNode): PNode =
-  if body.kind == nkEmpty or gCmd == cmdCompileToJS:
-    result = body
-  else:
-    # XXX implement it properly
-    result = body
+  # XXX implement it properly
+  result = body
 
 # ------------------- iterator transformation --------------------------------
 
-proc liftForLoop*(body: PNode; owner: PSym): PNode =
+proc liftForLoop*(g: ModuleGraph; body: PNode; owner: PSym): PNode =
   # problem ahead: the iterator could be invoked indirectly, but then
   # we don't know what environment to create here:
   #
@@ -873,10 +875,10 @@ proc liftForLoop*(body: PNode; owner: PSym): PNode =
         nkBreakState(cl.state)
         ...
     """
-  if liftingHarmful(owner): return body
+  if liftingHarmful(g.config, owner): return body
   var L = body.len
   if not (body.kind == nkForStmt and body[L-2].kind in nkCallKinds):
-    localError(body.info, "ignored invalid for loop")
+    localError(g.config, body.info, "ignored invalid for loop")
     return body
   var call = body[L-2]
 
@@ -889,7 +891,7 @@ proc liftForLoop*(body: PNode; owner: PSym): PNode =
     # createClosure()
     let iter = op.sym
 
-    let hp = getHiddenParam(iter)
+    let hp = getHiddenParam(g, iter)
     env = newSym(skLet, iter.name, owner, body.info)
     env.typ = hp.typ
     env.flags = hp.flags
@@ -898,7 +900,7 @@ proc liftForLoop*(body: PNode; owner: PSym): PNode =
     addVar(v, newSymNode(env))
     result.add(v)
     # add 'new' statement:
-    result.add(newCall(getSysSym"internalNew", env.newSymNode))
+    result.add(newCall(getSysSym(g, env.info, "internalNew"), env.newSymNode))
   elif op.kind == nkStmtListExpr:
     let closure = op.lastSon
     if closure.kind == nkClosure:
@@ -908,7 +910,7 @@ proc liftForLoop*(body: PNode; owner: PSym): PNode =
 
   var loopBody = newNodeI(nkStmtList, body.info, 3)
   var whileLoop = newNodeI(nkWhileStmt, body.info, 2)
-  whileLoop.sons[0] = newIntTypeNode(nkIntLit, 1, getSysType(tyBool))
+  whileLoop.sons[0] = newIntTypeNode(nkIntLit, 1, getSysType(g, body.info, tyBool))
   whileLoop.sons[1] = loopBody
   result.add whileLoop
 
@@ -923,7 +925,7 @@ proc liftForLoop*(body: PNode; owner: PSym): PNode =
 
   addSon(vpart, ast.emptyNode) # no explicit type
   if not env.isNil:
-    call.sons[0] = makeClosure(call.sons[0].sym, env.newSymNode, body.info)
+    call.sons[0] = makeClosure(g, call.sons[0].sym, env.newSymNode, body.info)
   addSon(vpart, call)
   addSon(v2, vpart)
 
diff --git a/compiler/lexer.nim b/compiler/lexer.nim
index e55da2f35..591561987 100644
--- a/compiler/lexer.nim
+++ b/compiler/lexer.nim
@@ -17,7 +17,7 @@
 
 import
   hashes, options, msgs, strutils, platform, idents, nimlexbase, llstream,
-  wordrecg
+  wordrecg, configuration
 
 const
   MaxLineLength* = 80         # lines longer than this lead to a warning
@@ -131,9 +131,9 @@ type
                                 # like 0b01 or  r"\L" are unaffected
       commentOffsetA*, commentOffsetB*: int
 
-  TErrorHandler* = proc (info: TLineInfo; msg: TMsgKind; arg: string)
+  TErrorHandler* = proc (conf: ConfigRef; info: TLineInfo; msg: TMsgKind; arg: string)
   TLexer* = object of TBaseLexer
-    fileIdx*: int32
+    fileIdx*: FileIndex
     indentAhead*: int         # if > 0 an indendation has already been read
                               # this is needed because scanning comments
                               # needs so much look-ahead
@@ -144,13 +144,12 @@ type
     cache*: IdentCache
     when defined(nimsuggest):
       previousToken: TLineInfo
+    config*: ConfigRef
 
 when defined(nimpretty):
   var
     gIndentationWidth*: int
 
-var gLinesCompiled*: int  # all lines that have been compiled
-
 proc getLineInfo*(L: TLexer, tok: TToken): TLineInfo {.inline.} =
   result = newLineInfo(L.fileIdx, tok.line, tok.col)
   when defined(nimpretty):
@@ -165,13 +164,12 @@ proc isKeyword*(kind: TTokType): bool =
 template ones(n): untyped = ((1 shl n)-1) # for utf-8 conversion
 
 proc isNimIdentifier*(s: string): bool =
-  if s[0] in SymStartChars:
+  let sLen = s.len
+  if sLen > 0 and s[0] in SymStartChars:
     var i = 1
-    var sLen = s.len
     while i < sLen:
-      if s[i] == '_':
-        inc(i)
-      if s[i] notin SymChars: return
+      if s[i] == '_': inc(i)
+      if i < sLen and s[i] notin SymChars: return
       inc(i)
     result = true
 
@@ -192,8 +190,8 @@ proc prettyTok*(tok: TToken): string =
   if isKeyword(tok.tokType): result = "keyword " & tok.ident.s
   else: result = tokToStr(tok)
 
-proc printTok*(tok: TToken) =
-  msgWriteln($tok.line & ":" & $tok.col & "\t" &
+proc printTok*(conf: ConfigRef; tok: TToken) =
+  msgWriteln(conf, $tok.line & ":" & $tok.col & "\t" &
       TokTypeToStr[tok.tokType] & " " & tokToStr(tok))
 
 proc initToken*(L: var TToken) =
@@ -222,8 +220,8 @@ proc fillToken(L: var TToken) =
     L.commentOffsetA = 0
     L.commentOffsetB = 0
 
-proc openLexer*(lex: var TLexer, fileIdx: int32, inputstream: PLLStream;
-                 cache: IdentCache) =
+proc openLexer*(lex: var TLexer, fileIdx: FileIndex, inputstream: PLLStream;
+                 cache: IdentCache; config: ConfigRef) =
   openBaseLexer(lex, inputstream)
   lex.fileIdx = fileidx
   lex.indentAhead = - 1
@@ -232,13 +230,15 @@ proc openLexer*(lex: var TLexer, fileIdx: int32, inputstream: PLLStream;
   lex.cache = cache
   when defined(nimsuggest):
     lex.previousToken.fileIndex = fileIdx
+  lex.config = config
 
 proc openLexer*(lex: var TLexer, filename: string, inputstream: PLLStream;
-                cache: IdentCache) =
-  openLexer(lex, filename.fileInfoIdx, inputstream, cache)
+                cache: IdentCache; config: ConfigRef) =
+  openLexer(lex, fileInfoIdx(config, filename), inputstream, cache, config)
 
 proc closeLexer*(lex: var TLexer) =
-  inc(gLinesCompiled, lex.lineNumber)
+  if lex.config != nil:
+    inc(lex.config.linesCompiled, lex.lineNumber)
   closeBaseLexer(lex)
 
 proc getLineInfo(L: TLexer): TLineInfo =
@@ -246,9 +246,9 @@ proc getLineInfo(L: TLexer): TLineInfo =
 
 proc dispMessage(L: TLexer; info: TLineInfo; msg: TMsgKind; arg: string) =
   if L.errorHandler.isNil:
-    msgs.message(info, msg, arg)
+    msgs.message(L.config, info, msg, arg)
   else:
-    L.errorHandler(info, msg, arg)
+    L.errorHandler(L.config, info, msg, arg)
 
 proc lexMessage*(L: TLexer, msg: TMsgKind, arg = "") =
   L.dispMessage(getLineInfo(L), msg, arg)
@@ -274,7 +274,7 @@ 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 and gIdeCmd in {ideSug, ideCon}:
+        L.lineNumber == gTrackPos.line.int and L.config.ideCmd in {ideSug, ideCon}:
       L.cursor = CursorPosition.InToken
       gTrackPos.col = colA.int16
     colA = 0
@@ -285,9 +285,9 @@ 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 and gIdeCmd in {ideSug, ideCon}:
+        L.lineNumber == gTrackPos.line.int and L.config.ideCmd in {ideSug, ideCon}:
       gTrackPos.fileIndex = trackPosInvalidFileIdx
-      gTrackPos.line = -1
+      gTrackPos.line = 0'u16
     colA = 0
   when defined(nimpretty):
     tok.offsetB = L.offsetBase + pos
@@ -299,7 +299,7 @@ template tokenEndPrevious(tok, pos) =
     # 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 and gIdeCmd in {ideSug, ideCon}:
+        L.lineNumber == gTrackPos.line.int and L.config.ideCmd in {ideSug, ideCon}:
       L.cursor = CursorPosition.BeforeToken
       gTrackPos = L.previousToken
       gTrackPosAttached = true
@@ -311,12 +311,12 @@ template tokenEndPrevious(tok, pos) =
 # We need to parse the largest uint literal without overflow checks
 proc unsafeParseUInt(s: string, b: var BiggestInt, start = 0): int =
   var i = start
-  if s[i] in {'0'..'9'}:
+  if i < s.len and s[i] in {'0'..'9'}:
     b = 0
-    while s[i] in {'0'..'9'}:
+    while i < s.len and s[i] in {'0'..'9'}:
       b = b * 10 + (ord(s[i]) - ord('0'))
       inc(i)
-      while s[i] == '_': inc(i) # underscores are allowed and ignored
+      while i < s.len and s[i] == '_': inc(i) # underscores are allowed and ignored
     result = i - start
 {.pop.} # overflowChecks
 
@@ -341,7 +341,8 @@ proc getNumber(L: var TLexer, result: var TToken) =
         break
       if buf[pos] == '_':
         if buf[pos+1] notin chars:
-          lexMessage(L, errInvalidToken, "_")
+          lexMessage(L, errGenerated,
+            "only single underscores may occur in a token: '__' is invalid")
           break
         add(tok.literal, '_')
         inc(pos)
@@ -355,7 +356,7 @@ proc getNumber(L: var TLexer, result: var TToken) =
       inc(pos)
     L.bufpos = pos
 
-  proc lexMessageLitNum(L: var TLexer, msg: TMsgKind, startpos: int) =
+  proc lexMessageLitNum(L: var TLexer, msg: string, startpos: int) =
     # Used to get slightly human friendlier err messages.
     # Note: the erroneous 'O' char in the character set is intentional
     const literalishChars = {'A'..'F', 'a'..'f', '0'..'9', 'X', 'x', 'o', 'O',
@@ -376,7 +377,7 @@ proc getNumber(L: var TLexer, result: var TToken) =
       add(t.literal, L.buf[L.bufpos])
       matchChars(L, t, {'0'..'9'})
     L.bufpos = msgPos
-    lexMessage(L, msg, t.literal)
+    lexMessage(L, errGenerated, msg % t.literal)
 
   var
     startpos, endpos: int
@@ -398,7 +399,7 @@ proc getNumber(L: var TLexer, result: var TToken) =
     eatChar(L, result, '0')
     case L.buf[L.bufpos]
     of 'O':
-      lexMessageLitNum(L, errInvalidNumberOctalCode, startpos)
+      lexMessageLitNum(L, "$1 is not a valid number; did you mean octal? Then use one of '0o', '0c' or '0C'.", startpos)
     of 'x', 'X':
       eatChar(L, result, 'x')
       matchUnderscoreChars(L, result, {'0'..'9', 'a'..'f', 'A'..'F'})
@@ -409,7 +410,7 @@ proc getNumber(L: var TLexer, result: var TToken) =
       eatChar(L, result, 'b')
       matchUnderscoreChars(L, result, {'0'..'1'})
     else:
-      internalError(getLineInfo(L), "getNumber")
+      internalError(L.config, getLineInfo(L), "getNumber")
   else:
     matchUnderscoreChars(L, result, {'0'..'9'})
     if (L.buf[L.bufpos] == '.') and (L.buf[L.bufpos + 1] in {'0'..'9'}):
@@ -464,7 +465,7 @@ proc getNumber(L: var TLexer, result: var TToken) =
         result.tokType = tkInt8Lit
         inc(postPos)
       else:
-        lexMessageLitNum(L, errInvalidNumber, startpos)
+        lexMessageLitNum(L, "invalid number: '$1'", startpos)
     of 'u', 'U':
       inc(postPos)
       if (L.buf[postPos] == '6') and (L.buf[postPos + 1] == '4'):
@@ -482,12 +483,12 @@ proc getNumber(L: var TLexer, result: var TToken) =
       else:
         result.tokType = tkUIntLit
     else:
-      lexMessageLitNum(L, errInvalidNumber, startpos)
+      lexMessageLitNum(L, "invalid number: '$1'", startpos)
 
   # Is there still a literalish char awaiting? Then it's an error!
   if  L.buf[postPos] in literalishChars or
      (L.buf[postPos] == '.' and L.buf[postPos + 1] in {'0'..'9'}):
-    lexMessageLitNum(L, errInvalidNumber, startpos)
+    lexMessageLitNum(L, "invalid number: '$1'", startpos)
 
   # Third stage, extract actual number
   L.bufpos = startpos            # restore position
@@ -528,7 +529,7 @@ proc getNumber(L: var TLexer, result: var TToken) =
           else:
             break
       else:
-        internalError(getLineInfo(L), "getNumber")
+        internalError(L.config, getLineInfo(L), "getNumber")
 
       case result.tokType
       of tkIntLit, tkInt64Lit: result.iNumber = xi
@@ -545,7 +546,7 @@ proc getNumber(L: var TLexer, result: var TToken) =
         # XXX: Test this on big endian machine!
       of tkFloat64Lit, tkFloatLit:
         result.fNumber = (cast[PFloat64](addr(xi)))[]
-      else: internalError(getLineInfo(L), "getNumber")
+      else: internalError(L.config, getLineInfo(L), "getNumber")
 
       # Bounds checks. Non decimal literals are allowed to overflow the range of
       # the datatype as long as their pattern don't overflow _bitwise_, hence
@@ -561,7 +562,7 @@ proc getNumber(L: var TLexer, result: var TToken) =
 
         if outOfRange:
           #echo "out of range num: ", result.iNumber, " vs ", xi
-          lexMessageLitNum(L, errNumberOutOfRange, startpos)
+          lexMessageLitNum(L, "number out of range: '$1'", startpos)
 
     else:
       case result.tokType
@@ -577,19 +578,20 @@ proc getNumber(L: var TLexer, result: var TToken) =
         result.iNumber = parseBiggestInt(result.literal)
 
       # Explicit bounds checks
-      let outOfRange = case result.tokType:
-      of tkInt8Lit: (result.iNumber < int8.low or result.iNumber > int8.high)
-      of tkUInt8Lit: (result.iNumber < BiggestInt(uint8.low) or
-                      result.iNumber > BiggestInt(uint8.high))
-      of tkInt16Lit: (result.iNumber < int16.low or result.iNumber > int16.high)
-      of tkUInt16Lit: (result.iNumber < BiggestInt(uint16.low) or
-                      result.iNumber > BiggestInt(uint16.high))
-      of tkInt32Lit: (result.iNumber < int32.low or result.iNumber > int32.high)
-      of tkUInt32Lit: (result.iNumber < BiggestInt(uint32.low) or
-                      result.iNumber > BiggestInt(uint32.high))
-      else: false
-
-      if outOfRange: lexMessageLitNum(L, errNumberOutOfRange, startpos)
+      let outOfRange =
+        case result.tokType
+        of tkInt8Lit: (result.iNumber < int8.low or result.iNumber > int8.high)
+        of tkUInt8Lit: (result.iNumber < BiggestInt(uint8.low) or
+                        result.iNumber > BiggestInt(uint8.high))
+        of tkInt16Lit: (result.iNumber < int16.low or result.iNumber > int16.high)
+        of tkUInt16Lit: (result.iNumber < BiggestInt(uint16.low) or
+                        result.iNumber > BiggestInt(uint16.high))
+        of tkInt32Lit: (result.iNumber < int32.low or result.iNumber > int32.high)
+        of tkUInt32Lit: (result.iNumber < BiggestInt(uint32.low) or
+                        result.iNumber > BiggestInt(uint32.high))
+        else: false
+
+      if outOfRange: lexMessageLitNum(L, "number out of range: '$1'", startpos)
 
     # Promote int literal to int64? Not always necessary, but more consistent
     if result.tokType == tkIntLit:
@@ -597,9 +599,9 @@ proc getNumber(L: var TLexer, result: var TToken) =
         result.tokType = tkInt64Lit
 
   except ValueError:
-    lexMessageLitNum(L, errInvalidNumber, startpos)
+    lexMessageLitNum(L, "invalid number: '$1'", startpos)
   except OverflowError, RangeError:
-    lexMessageLitNum(L, errNumberOutOfRange, startpos)
+    lexMessageLitNum(L, "number out of range: '$1'", startpos)
   tokenEnd(result, postPos-1)
   L.bufpos = postPos
 
@@ -625,8 +627,9 @@ proc getEscapedChar(L: var TLexer, tok: var TToken) =
   inc(L.bufpos)               # skip '\'
   case L.buf[L.bufpos]
   of 'n', 'N':
-    if gOldNewlines:
-      if tok.tokType == tkCharLit: lexMessage(L, errNnotAllowedInCharacter)
+    if L.config.oldNewlines:
+      if tok.tokType == tkCharLit:
+        lexMessage(L, errGenerated, "\\n not allowed in character literal")
       add(tok.literal, tnl)
     else:
       add(tok.literal, '\L')
@@ -695,8 +698,8 @@ proc getEscapedChar(L: var TLexer, tok: var TToken) =
     var xi = 0
     handleDecChars(L, xi)
     if (xi <= 255): add(tok.literal, chr(xi))
-    else: lexMessage(L, errInvalidCharacterConstant)
-  else: lexMessage(L, errInvalidCharacterConstant)
+    else: lexMessage(L, errGenerated, "invalid character constant")
+  else: lexMessage(L, errGenerated, "invalid character constant")
 
 proc newString(s: cstring, len: int): string =
   ## XXX, how come there is no support for this?
@@ -711,7 +714,7 @@ proc handleCRLF(L: var TLexer, pos: int): int =
     if col > MaxLineLength:
       lexMessagePos(L, hintLineTooLong, pos)
 
-    if optEmbedOrigSrc in gGlobalOptions:
+    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)
@@ -760,7 +763,7 @@ proc getString(L: var TLexer, tok: var TToken, rawMode: bool) =
         tokenEndIgnore(tok, pos)
         var line2 = L.lineNumber
         L.lineNumber = line
-        lexMessagePos(L, errClosingTripleQuoteExpected, L.lineStart)
+        lexMessagePos(L, errGenerated, L.lineStart, "closing \"\"\" expected, but end of file reached")
         L.lineNumber = line2
         L.bufpos = pos
         break
@@ -783,7 +786,7 @@ proc getString(L: var TLexer, tok: var TToken, rawMode: bool) =
           break
       elif c in {CR, LF, nimlexbase.EndOfFile}:
         tokenEndIgnore(tok, pos)
-        lexMessage(L, errClosingQuoteExpected)
+        lexMessage(L, errGenerated, "closing \" expected")
         break
       elif (c == '\\') and not rawMode:
         L.bufpos = pos
@@ -799,12 +802,13 @@ proc getCharacter(L: var TLexer, tok: var TToken) =
   inc(L.bufpos)               # skip '
   var c = L.buf[L.bufpos]
   case c
-  of '\0'..pred(' '), '\'': lexMessage(L, errInvalidCharacterConstant)
+  of '\0'..pred(' '), '\'': lexMessage(L, errGenerated, "invalid character literal")
   of '\\': getEscapedChar(L, tok)
   else:
     tok.literal = $c
     inc(L.bufpos)
-  if L.buf[L.bufpos] != '\'': lexMessage(L, errMissingFinalQuote)
+  if L.buf[L.bufpos] != '\'':
+    lexMessage(L, errGenerated, "missing closing ' for character literal")
   tokenEndIgnore(tok, L.bufpos)
   inc(L.bufpos)               # skip '
 
@@ -825,7 +829,7 @@ proc getSymbol(L: var TLexer, tok: var TToken) =
       inc(pos)
     of '_':
       if buf[pos+1] notin SymChars:
-        lexMessage(L, errInvalidToken, "_")
+        lexMessage(L, errGenerated, "invalid token: trailing underscore")
         break
       inc(pos)
     else: break
@@ -1003,13 +1007,17 @@ proc skip(L: var TLexer, tok: var TToken) =
   var buf = L.buf
   tokenBegin(tok, pos)
   tok.strongSpaceA = 0
+  when defined(nimpretty):
+    var hasComment = false
+    tok.commentOffsetA = L.offsetBase + pos
+    tok.commentOffsetB = tok.commentOffsetA
   while true:
     case buf[pos]
     of ' ':
       inc(pos)
       inc(tok.strongSpaceA)
     of '\t':
-      if not L.allowTabs: lexMessagePos(L, errTabulatorsAreNotAllowed, pos)
+      if not L.allowTabs: lexMessagePos(L, errGenerated, pos, "tabulators are not allowed")
       inc(pos)
     of CR, LF:
       tokenEndPrevious(tok, pos)
@@ -1021,6 +1029,7 @@ proc skip(L: var TLexer, tok: var TToken) =
           inc(pos)
           inc(indent)
         elif buf[pos] == '#' and buf[pos+1] == '[':
+          when defined(nimpretty): hasComment = true
           skipMultiLineComment(L, tok, pos+2, false)
           pos = L.bufpos
           buf = L.buf
@@ -1034,14 +1043,11 @@ proc skip(L: var TLexer, tok: var TToken) =
     of '#':
       # do not skip documentation comment:
       if buf[pos+1] == '#': break
-      when defined(nimpretty):
-        tok.commentOffsetA = L.offsetBase + pos
+      when defined(nimpretty): hasComment = true
       if buf[pos+1] == '[':
         skipMultiLineComment(L, tok, pos+2, false)
         pos = L.bufpos
         buf = L.buf
-        when defined(nimpretty):
-          tok.commentOffsetB = L.offsetBase + pos
       else:
         tokenBegin(tok, pos)
         while buf[pos] notin {CR, LF, nimlexbase.EndOfFile}: inc(pos)
@@ -1053,6 +1059,9 @@ proc skip(L: var TLexer, tok: var TToken) =
   tokenEndPrevious(tok, pos-1)
   L.bufpos = pos
   when defined(nimpretty):
+    if hasComment:
+      tok.commentOffsetB = L.offsetBase + pos
+      tok.tokType = tkComment
     if gIndentationWidth <= 0:
       gIndentationWidth = tok.indent
 
@@ -1061,7 +1070,7 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) =
     when defined(nimsuggest):
       # we attach the cursor to the last *strong* token
       if tok.tokType notin weakTokens:
-        L.previousToken.line = tok.line.int16
+        L.previousToken.line = tok.line.uint16
         L.previousToken.col = tok.col.int16
 
   when defined(nimsuggest):
@@ -1074,6 +1083,10 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) =
   else:
     tok.indent = -1
   skip(L, tok)
+  when defined(nimpretty):
+    if tok.tokType == tkComment:
+      L.indentAhead = L.currLineIndent
+      return
   var c = L.buf[L.bufpos]
   tok.line = L.lineNumber
   tok.col = getColNumber(L, L.bufpos)
@@ -1109,7 +1122,7 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) =
         tok.tokType = tkParLe
         when defined(nimsuggest):
           if L.fileIdx == gTrackPos.fileIndex and tok.col < gTrackPos.col and
-                    tok.line == gTrackPos.line and gIdeCmd == ideCon:
+                    tok.line == gTrackPos.line.int and L.config.ideCmd == ideCon:
             gTrackPos.col = tok.col.int16
     of ')':
       tok.tokType = tkParRi
@@ -1130,7 +1143,7 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) =
     of '.':
       when defined(nimsuggest):
         if L.fileIdx == gTrackPos.fileIndex and tok.col+1 == gTrackPos.col and
-            tok.line == gTrackPos.line and gIdeCmd == ideSug:
+            tok.line == gTrackPos.line.int and L.config.ideCmd == ideSug:
           tok.tokType = tkDot
           L.cursor = CursorPosition.InToken
           gTrackPos.col = tok.col.int16
@@ -1172,7 +1185,7 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) =
       else:
         tok.literal = $c
         tok.tokType = tkInvalid
-        lexMessage(L, errInvalidToken, c & " (\\" & $(ord(c)) & ')')
+        lexMessage(L, errGenerated, "invalid token: " & c & " (\\" & $(ord(c)) & ')')
     of '\"':
       # check for extended raw string literal:
       var rawMode = L.bufpos > 0 and L.buf[L.bufpos-1] in SymChars
@@ -1189,7 +1202,7 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) =
       getNumber(L, tok)
       let c = L.buf[L.bufpos]
       if c in SymChars+{'_'}:
-        lexMessage(L, errInvalidToken, c & " (\\" & $(ord(c)) & ')')
+        lexMessage(L, errGenerated, "invalid token: no whitespace between number and identifier")
     else:
       if c in OpChars:
         getOperator(L, tok)
@@ -1199,6 +1212,6 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) =
       else:
         tok.literal = $c
         tok.tokType = tkInvalid
-        lexMessage(L, errInvalidToken, c & " (\\" & $(ord(c)) & ')')
+        lexMessage(L, errGenerated, "invalid token: " & c & " (\\" & $(ord(c)) & ')')
         inc(L.bufpos)
   atTokenEnd()
diff --git a/compiler/liftlocals.nim b/compiler/liftlocals.nim
index 3610a1486..4603d357b 100644
--- a/compiler/liftlocals.nim
+++ b/compiler/liftlocals.nim
@@ -52,17 +52,17 @@ 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): PNode =
+proc liftLocalsIfRequested*(prc: PSym; n: PNode; conf: ConfigRef): PNode =
   let liftDest = getPragmaVal(prc.ast, wLiftLocals)
   if liftDest == nil: return n
   let partialParam = lookupParam(prc.typ.n, liftDest)
   if partialParam.isNil:
-    localError(liftDest.info, "'$1' is not a parameter of '$2'" %
+    localError(conf, liftDest.info, "'$1' is not a parameter of '$2'" %
               [$liftDest, prc.name.s])
     return n
   let objType = partialParam.typ.skipTypes(abstractPtrs)
   if objType.kind != tyObject or tfPartial notin objType.flags:
-    localError(liftDest.info, "parameter '$1' is not a pointer to a partial object" % $liftDest)
+    localError(conf, liftDest.info, "parameter '$1' is not a pointer to a partial object" % $liftDest)
     return n
   var c = Ctx(partialParam: partialParam, objType: objType)
   let w = newTree(nkStmtList, n)
diff --git a/compiler/lookups.nim b/compiler/lookups.nim
index e0d6e0098..b6d63d0bd 100644
--- a/compiler/lookups.nim
+++ b/compiler/lookups.nim
@@ -11,23 +11,23 @@
 
 import
   intsets, ast, astalgo, idents, semdata, types, msgs, options, rodread,
-  renderer, wordrecg, idgen, nimfix.prettybase
+  renderer, wordrecg, idgen, nimfix.prettybase, configuration, strutils
 
-proc ensureNoMissingOrUnusedSymbols(scope: PScope)
+proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope)
 
-proc noidentError(n, origin: PNode) =
+proc noidentError(conf: ConfigRef; n, origin: PNode) =
   var m = ""
   if origin != nil:
     m.add "in expression '" & origin.renderTree & "': "
   m.add "identifier expected, but found '" & n.renderTree & "'"
-  localError(n.info, m)
+  localError(conf, n.info, m)
 
-proc considerQuotedIdent*(n: PNode, origin: PNode = nil): PIdent =
+proc considerQuotedIdent*(conf: ConfigRef; 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(n, origin)
+    noidentError(conf, n, origin)
     result = getIdent"<Error>"
 
   case n.kind
@@ -36,7 +36,7 @@ proc considerQuotedIdent*(n: PNode, origin: PNode = nil): PIdent =
   of nkAccQuoted:
     case n.len
     of 0: handleError(n, origin)
-    of 1: result = considerQuotedIdent(n.sons[0], origin)
+    of 1: result = considerQuotedIdent(conf, n.sons[0], origin)
     else:
       var id = ""
       for i in 0..<n.len:
@@ -71,7 +71,7 @@ proc rawCloseScope*(c: PContext) =
   c.currentScope = c.currentScope.parent
 
 proc closeScope*(c: PContext) =
-  ensureNoMissingOrUnusedSymbols(c.currentScope)
+  ensureNoMissingOrUnusedSymbols(c, c.currentScope)
   rawCloseScope(c)
 
 iterator walkScopes*(scope: PScope): PScope =
@@ -80,15 +80,15 @@ iterator walkScopes*(scope: PScope): PScope =
     yield current
     current = current.parent
 
-proc skipAlias*(s: PSym; n: PNode): PSym =
+proc skipAlias*(s: PSym; n: PNode; conf: ConfigRef): PSym =
   if s == nil or s.kind != skAlias:
     result = s
   else:
     result = s.owner
-    if gCmd == cmdPretty:
+    if conf.cmd == cmdPretty:
       prettybase.replaceDeprecated(n.info, s, result)
     else:
-      message(n.info, warnDeprecated, "use " & result.name.s & " instead; " &
+      message(conf, n.info, warnDeprecated, "use " & result.name.s & " instead; " &
               s.name.s)
 
 proc localSearchInScope*(c: PContext, s: PIdent): PSym =
@@ -125,14 +125,14 @@ 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(m)
+      considerQuotedIdent(c.config, m)
     else:
       getIdent("err:" & renderTree(m))
-  result = newSym(skError, ident, getCurrOwner(c), n.info)
+  result = newSym(skError, ident, getCurrOwner(c), n.info, {})
   result.typ = errorType(c)
   incl(result.flags, sfDiscardable)
   # pretend it's imported from some unknown module to prevent cascading errors:
-  if gCmd != cmdInteractive and c.compilesContextId == 0:
+  if c.config.cmd != cmdInteractive and c.compilesContextId == 0:
     c.importTable.addSym(result)
 
 type
@@ -154,7 +154,7 @@ proc getSymRepr*(s: PSym): string =
   else:
     result = s.name.s
 
-proc ensureNoMissingOrUnusedSymbols(scope: PScope) =
+proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope) =
   # check if all symbols have been used and defined:
   var it: TTabIter
   var s = initTabIter(it, scope.symbols)
@@ -164,54 +164,53 @@ proc ensureNoMissingOrUnusedSymbols(scope: PScope) =
       # too many 'implementation of X' errors are annoying
       # and slow 'suggest' down:
       if missingImpls == 0:
-        localError(s.info, errImplOfXexpected, getSymRepr(s))
+        localError(c.config, s.info, "implementation of '$1' expected" % getSymRepr(s))
       inc missingImpls
     elif {sfUsed, sfExported} * s.flags == {} and optHints in s.options:
-      # BUGFIX: check options in s!
       if s.kind notin {skForVar, skParam, skMethod, skUnknown, skGenericParam}:
         # XXX: implicit type params are currently skTypes
         # maybe they can be made skGenericParam as well.
         if s.typ != nil and tfImplicitTypeParam notin s.typ.flags and
            s.typ.kind != tyGenericParam:
-          message(s.info, hintXDeclaredButNotUsed, getSymRepr(s))
+          message(c.config, s.info, hintXDeclaredButNotUsed, getSymRepr(s))
     s = nextIter(it, scope.symbols)
 
-proc wrongRedefinition*(info: TLineInfo, s: string) =
-  if gCmd != cmdInteractive:
-    localError(info, errAttemptToRedefine, s)
+proc wrongRedefinition*(c: PContext; info: TLineInfo, s: string) =
+  if c.config.cmd != cmdInteractive:
+    localError(c.config, info, "redefinition of '$1'" % s)
 
 proc addDecl*(c: PContext, sym: PSym, info: TLineInfo) =
   if not c.currentScope.addUniqueSym(sym):
-    wrongRedefinition(info, sym.name.s)
+    wrongRedefinition(c, info, sym.name.s)
 
 proc addDecl*(c: PContext, sym: PSym) =
   if not c.currentScope.addUniqueSym(sym):
-    wrongRedefinition(sym.info, sym.name.s)
+    wrongRedefinition(c, sym.info, sym.name.s)
 
 proc addPrelimDecl*(c: PContext, sym: PSym) =
   discard c.currentScope.addUniqueSym(sym)
 
-proc addDeclAt*(scope: PScope, sym: PSym) =
+proc addDeclAt*(c: PContext; scope: PScope, sym: PSym) =
   if not scope.addUniqueSym(sym):
-    wrongRedefinition(sym.info, sym.name.s)
+    wrongRedefinition(c, sym.info, sym.name.s)
 
 proc addInterfaceDeclAux(c: PContext, sym: PSym) =
   if sfExported in sym.flags:
     # add to interface:
     if c.module != nil: strTableAdd(c.module.tab, sym)
-    else: internalError(sym.info, "addInterfaceDeclAux")
+    else: internalError(c.config, sym.info, "addInterfaceDeclAux")
 
 proc addInterfaceDeclAt*(c: PContext, scope: PScope, sym: PSym) =
-  addDeclAt(scope, sym)
+  addDeclAt(c, scope, sym)
   addInterfaceDeclAux(c, sym)
 
-proc addOverloadableSymAt*(scope: PScope, fn: PSym) =
+proc addOverloadableSymAt*(c: PContext; scope: PScope, fn: PSym) =
   if fn.kind notin OverloadableSyms:
-    internalError(fn.info, "addOverloadableSymAt")
+    internalError(c.config, fn.info, "addOverloadableSymAt")
     return
   let check = strTableGet(scope.symbols, fn.name)
   if check != nil and check.kind notin OverloadableSyms:
-    wrongRedefinition(fn.info, fn.name.s)
+    wrongRedefinition(c, fn.info, fn.name.s)
   else:
     scope.addSym(fn)
 
@@ -222,12 +221,10 @@ proc addInterfaceDecl*(c: PContext, sym: PSym) =
 
 proc addInterfaceOverloadableSymAt*(c: PContext, scope: PScope, sym: PSym) =
   # it adds the symbol to the interface if appropriate
-  addOverloadableSymAt(scope, sym)
+  addOverloadableSymAt(c, scope, sym)
   addInterfaceDeclAux(c, sym)
 
 when defined(nimfix):
-  import strutils
-
   # when we cannot find the identifier, retry with a changed identifer:
   proc altSpelling(x: PIdent): PIdent =
     case x.s[0]
@@ -255,7 +252,7 @@ proc errorUseQualifier*(c: PContext; info: TLineInfo; s: PSym) =
     err.add candidate.owner.name.s & "." & candidate.name.s
     candidate = nextIdentIter(ti, c.importTable.symbols)
     inc i
-  localError(info, errGenerated, err)
+  localError(c.config, info, errGenerated, err)
 
 proc errorUndeclaredIdentifier*(c: PContext; info: TLineInfo; name: string) =
   var err = "undeclared identifier: '" & name & "'"
@@ -264,13 +261,13 @@ proc errorUndeclaredIdentifier*(c: PContext; info: TLineInfo; name: string) =
     err.add c.recursiveDep
     # prevent excessive errors for 'nim check'
     c.recursiveDep = nil
-  localError(info, errGenerated, err)
+  localError(c.config, info, errGenerated, err)
 
 proc lookUp*(c: PContext, n: PNode): PSym =
   # Looks up a symbol. Generates an error in case of nil.
   case n.kind
   of nkIdent:
-    result = searchInScopes(c, n.ident).skipAlias(n)
+    result = searchInScopes(c, n.ident).skipAlias(n, c.config)
     if result == nil:
       fixSpelling(n, n.ident, searchInScopes)
       errorUndeclaredIdentifier(c, n.info, n.ident.s)
@@ -278,14 +275,14 @@ proc lookUp*(c: PContext, n: PNode): PSym =
   of nkSym:
     result = n.sym
   of nkAccQuoted:
-    var ident = considerQuotedIdent(n)
-    result = searchInScopes(c, ident).skipAlias(n)
+    var ident = considerQuotedIdent(c.config, n)
+    result = searchInScopes(c, ident).skipAlias(n, c.config)
     if result == nil:
       fixSpelling(n, ident, searchInScopes)
       errorUndeclaredIdentifier(c, n.info, ident.s)
       result = errorSym(c, n)
   else:
-    internalError(n.info, "lookUp")
+    internalError(c.config, n.info, "lookUp")
     return
   if contains(c.ambiguousSymbols, result.id):
     errorUseQualifier(c, n.info, result)
@@ -299,11 +296,11 @@ 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(n)
+    var ident = considerQuotedIdent(c.config, n)
     if checkModule in flags:
-      result = searchInScopes(c, ident).skipAlias(n)
+      result = searchInScopes(c, ident).skipAlias(n, c.config)
     else:
-      result = searchInScopes(c, ident, allExceptModule).skipAlias(n)
+      result = searchInScopes(c, ident, allExceptModule).skipAlias(n, c.config)
     if result == nil and checkPureEnumFields in flags:
       result = strTableGet(c.pureEnumFields, ident)
     if result == nil and checkUndeclared in flags:
@@ -325,12 +322,12 @@ 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(n.sons[1])
+        ident = considerQuotedIdent(c.config, n.sons[1])
       if ident != nil:
         if m == c.module:
-          result = strTableGet(c.topLevelScope.symbols, ident).skipAlias(n)
+          result = strTableGet(c.topLevelScope.symbols, ident).skipAlias(n, c.config)
         else:
-          result = strTableGet(m.tab, ident).skipAlias(n)
+          result = strTableGet(m.tab, ident).skipAlias(n, c.config)
         if result == nil and checkUndeclared in flags:
           fixSpelling(n.sons[1], ident, searchInScopes)
           errorUndeclaredIdentifier(c, n.sons[1].info, ident.s)
@@ -339,7 +336,7 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
         result = n.sons[1].sym
       elif checkUndeclared in flags and
            n.sons[1].kind notin {nkOpenSymChoice, nkClosedSymChoice}:
-        localError(n.sons[1].info, errIdentifierExpected,
+        localError(c.config, n.sons[1].info, "identifier expected, but got: " &
                    renderTree(n.sons[1]))
         result = errorSym(c, n.sons[1])
   else:
@@ -349,11 +346,11 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
 proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
   case n.kind
   of nkIdent, nkAccQuoted:
-    var ident = considerQuotedIdent(n)
+    var ident = considerQuotedIdent(c.config, n)
     o.scope = c.currentScope
     o.mode = oimNoQualifier
     while true:
-      result = initIdentIter(o.it, o.scope.symbols, ident).skipAlias(n)
+      result = initIdentIter(o.it, o.scope.symbols, ident).skipAlias(n, c.config)
       if result != nil:
         break
       else:
@@ -370,17 +367,17 @@ 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(n.sons[1], n)
+        ident = considerQuotedIdent(c.config, n.sons[1], n)
       if ident != nil:
         if o.m == c.module:
           # a module may access its private members:
           result = initIdentIter(o.it, c.topLevelScope.symbols,
-                                 ident).skipAlias(n)
+                                 ident).skipAlias(n, c.config)
           o.mode = oimSelfModule
         else:
-          result = initIdentIter(o.it, o.m.tab, ident).skipAlias(n)
+          result = initIdentIter(o.it, o.m.tab, ident).skipAlias(n, c.config)
       else:
-        noidentError(n.sons[1], n)
+        noidentError(c.config, n.sons[1], n)
         result = errorSym(c, n.sons[1])
   of nkClosedSymChoice, nkOpenSymChoice:
     o.mode = oimSymChoice
@@ -408,18 +405,18 @@ proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
     result = nil
   of oimNoQualifier:
     if o.scope != nil:
-      result = nextIdentIter(o.it, o.scope.symbols).skipAlias(n)
+      result = nextIdentIter(o.it, o.scope.symbols).skipAlias(n, c.config)
       while result == nil:
         o.scope = o.scope.parent
         if o.scope == nil: break
-        result = initIdentIter(o.it, o.scope.symbols, o.it.name).skipAlias(n)
+        result = initIdentIter(o.it, o.scope.symbols, o.it.name).skipAlias(n, c.config)
         # BUGFIX: o.it.name <-> n.ident
     else:
       result = nil
   of oimSelfModule:
-    result = nextIdentIter(o.it, c.topLevelScope.symbols).skipAlias(n)
+    result = nextIdentIter(o.it, c.topLevelScope.symbols).skipAlias(n, c.config)
   of oimOtherModule:
-    result = nextIdentIter(o.it, o.m.tab).skipAlias(n)
+    result = nextIdentIter(o.it, o.m.tab).skipAlias(n, c.config)
   of oimSymChoice:
     if o.symChoiceIndex < sonsLen(n):
       result = n.sons[o.symChoiceIndex].sym
@@ -430,19 +427,19 @@ proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
       o.mode = oimSymChoiceLocalLookup
       o.scope = c.currentScope
       result = firstIdentExcluding(o.it, o.scope.symbols,
-                                   n.sons[0].sym.name, o.inSymChoice).skipAlias(n)
+                                   n.sons[0].sym.name, o.inSymChoice).skipAlias(n, c.config)
       while result == nil:
         o.scope = o.scope.parent
         if o.scope == nil: break
         result = firstIdentExcluding(o.it, o.scope.symbols,
-                                     n.sons[0].sym.name, o.inSymChoice).skipAlias(n)
+                                     n.sons[0].sym.name, o.inSymChoice).skipAlias(n, c.config)
   of oimSymChoiceLocalLookup:
-    result = nextIdentExcluding(o.it, o.scope.symbols, o.inSymChoice).skipAlias(n)
+    result = nextIdentExcluding(o.it, o.scope.symbols, o.inSymChoice).skipAlias(n, c.config)
     while result == nil:
       o.scope = o.scope.parent
       if o.scope == nil: break
       result = firstIdentExcluding(o.it, o.scope.symbols,
-                                   n.sons[0].sym.name, o.inSymChoice).skipAlias(n)
+                                   n.sons[0].sym.name, o.inSymChoice).skipAlias(n, c.config)
 
   if result != nil and result.kind == skStub: loadStub(result)
 
diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim
index 8510bf7ee..13336f00e 100644
--- a/compiler/lowerings.nim
+++ b/compiler/lowerings.nim
@@ -12,18 +12,18 @@
 const
   genPrefix* = ":tmp"         # prefix for generated names
 
-import ast, astalgo, types, idents, magicsys, msgs, options
+import ast, astalgo, types, idents, magicsys, msgs, options, modulegraphs
 from trees import getMagic
 
 proc newDeref*(n: PNode): PNode {.inline.} =
   result = newNodeIT(nkHiddenDeref, n.info, n.typ.sons[0])
   addSon(result, n)
 
-proc newTupleAccess*(tup: PNode, i: int): PNode =
+proc newTupleAccess*(g: ModuleGraph; tup: PNode, i: int): PNode =
   result = newNodeIT(nkBracketExpr, tup.info, tup.typ.skipTypes(
                      abstractInst).sons[i])
   addSon(result, copyTree(tup))
-  var lit = newNodeIT(nkIntLit, tup.info, getSysType(tyInt))
+  var lit = newNodeIT(nkIntLit, tup.info, getSysType(g, tup.info, tyInt))
   lit.intVal = i
   addSon(result, lit)
 
@@ -44,12 +44,12 @@ proc newFastAsgnStmt(le, ri: PNode): PNode =
   result.sons[0] = le
   result.sons[1] = ri
 
-proc lowerTupleUnpacking*(n: PNode; owner: PSym): PNode =
+proc lowerTupleUnpacking*(g: ModuleGraph; n: PNode; owner: PSym): PNode =
   assert n.kind == nkVarTuple
   let value = n.lastSon
   result = newNodeI(nkStmtList, n.info)
 
-  var temp = newSym(skTemp, getIdent(genPrefix), owner, value.info)
+  var temp = newSym(skTemp, getIdent(genPrefix), owner, value.info, g.config.options)
   temp.typ = skipTypes(value.typ, abstractInst)
   incl(temp.flags, sfFromGeneric)
 
@@ -61,7 +61,7 @@ proc lowerTupleUnpacking*(n: PNode; owner: PSym): PNode =
   result.add newAsgnStmt(tempAsNode, value)
   for i in 0 .. n.len-3:
     if n.sons[i].kind == nkSym: v.addVar(n.sons[i])
-    result.add newAsgnStmt(n.sons[i], newTupleAccess(tempAsNode, i))
+    result.add newAsgnStmt(n.sons[i], newTupleAccess(g, tempAsNode, i))
 
 proc newTupleAccessRaw*(tup: PNode, i: int): PNode =
   result = newNodeI(nkBracketExpr, tup.info)
@@ -77,7 +77,7 @@ proc lowerTupleUnpackingForAsgn*(n: PNode; owner: PSym): PNode =
   let value = n.lastSon
   result = newNodeI(nkStmtList, n.info)
 
-  var temp = newSym(skLet, getIdent("_"), owner, value.info)
+  var temp = newSym(skLet, getIdent("_"), owner, value.info, owner.options)
   var v = newNodeI(nkLetSection, value.info)
   let tempAsNode = newSymNode(temp) #newIdentNode(getIdent(genPrefix & $temp.id), value.info)
 
@@ -95,7 +95,7 @@ proc lowerTupleUnpackingForAsgn*(n: PNode; owner: PSym): PNode =
 proc lowerSwap*(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)
+  var temp = newSym(skVar, getIdent(genPrefix), owner, n.info, owner.options)
   temp.typ = n.sons[1].typ
   incl(temp.flags, sfFromGeneric)
 
@@ -112,16 +112,16 @@ proc lowerSwap*(n: PNode; owner: PSym): PNode =
   result.add newFastAsgnStmt(n[1], n[2])
   result.add newFastAsgnStmt(n[2], tempAsNode)
 
-proc createObj*(owner: PSym, info: TLineInfo; final=true): PType =
+proc createObj*(g: ModuleGraph; owner: PSym, info: TLineInfo; final=true): PType =
   result = newType(tyObject, owner)
   if final:
     rawAddSon(result, nil)
     incl result.flags, tfFinal
   else:
-    rawAddSon(result, getCompilerProc("RootObj").typ)
+    rawAddSon(result, getCompilerProc(g, "RootObj").typ)
   result.n = newNodeI(nkRecList, info)
   let s = newSym(skType, getIdent("Env_" & info.toFilename),
-                  owner, info)
+                  owner, info, owner.options)
   incl s.flags, sfAnon
   s.typ = result
   result.sym = s
@@ -174,7 +174,8 @@ proc lookupInRecord(n: PNode, id: int): PSym =
 proc addField*(obj: PType; s: PSym) =
   # 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(s.name.s & $obj.n.len), s.owner, s.info,
+                     s.options)
   field.id = -s.id
   let t = skipIntLit(s.typ)
   field.typ = t
@@ -185,7 +186,8 @@ proc addField*(obj: PType; s: PSym) =
 proc addUniqueField*(obj: PType; s: PSym): 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(s.name.s & $obj.n.len), s.owner, s.info,
+                       s.options)
     field.id = -s.id
     let t = skipIntLit(s.typ)
     field.typ = t
@@ -218,7 +220,7 @@ proc indirectAccess*(a: PNode, b: int, info: TLineInfo): PNode =
   #if field == nil:
   #  echo "FIELD ", b
   #  debug deref.typ
-  internalAssert field != nil
+  assert field != nil
   addSon(deref, a)
   result = newNodeI(nkDotExpr, info)
   addSon(result, deref)
@@ -242,7 +244,7 @@ proc indirectAccess(a: PNode, b: string, info: TLineInfo): PNode =
   #if field == nil:
   #  echo "FIELD ", b
   #  debug deref.typ
-  internalAssert field != nil
+  assert field != nil
   addSon(deref, a)
   result = newNodeI(nkDotExpr, info)
   addSon(result, deref)
@@ -278,12 +280,12 @@ proc genDeref*(n: PNode): PNode =
                      n.typ.skipTypes(abstractInst).sons[0])
   result.add n
 
-proc callCodegenProc*(name: string, arg1: PNode;
+proc callCodegenProc*(g: ModuleGraph; name: string, arg1: PNode;
                       arg2, arg3, optionalArgs: PNode = nil): PNode =
   result = newNodeI(nkCall, arg1.info)
-  let sym = magicsys.getCompilerProc(name)
+  let sym = magicsys.getCompilerProc(g, name)
   if sym == nil:
-    localError(arg1.info, errSystemNeeds, name)
+    localError(g.config, arg1.info, "system module needs: " & name)
   else:
     result.add newSymNode(sym)
     result.add arg1
@@ -333,9 +335,10 @@ proc typeNeedsNoDeepCopy(t: PType): bool =
   if t.kind in {tyVar, tyLent, tySequence}: t = t.lastSon
   result = not containsGarbageCollectedRef(t)
 
-proc addLocalVar(varSection, varInit: PNode; owner: PSym; typ: PType;
+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(genPrefix), owner, varSection.info,
+                  owner.options)
   result.typ = typ
   incl(result.flags, sfFromGeneric)
 
@@ -349,7 +352,7 @@ proc addLocalVar(varSection, varInit: PNode; owner: PSym; typ: PType;
       varInit.add newFastAsgnStmt(newSymNode(result), v)
     else:
       let deepCopyCall = newNodeI(nkCall, varInit.info, 3)
-      deepCopyCall.sons[0] = newSymNode(getSysMagic("deepCopy", mDeepCopy))
+      deepCopyCall.sons[0] = newSymNode(getSysMagic(g, varSection.info, "deepCopy", mDeepCopy))
       deepCopyCall.sons[1] = newSymNode(result)
       deepCopyCall.sons[2] = v
       varInit.add deepCopyCall
@@ -384,23 +387,23 @@ stmtList:
 
 """
 
-proc createWrapperProc(f: PNode; threadParam, argsParam: PSym;
+proc createWrapperProc(g: ModuleGraph; f: PNode; threadParam, argsParam: PSym;
                        varSection, varInit, call, barrier, fv: PNode;
                        spawnKind: TSpawnResult): PSym =
   var body = newNodeI(nkStmtList, f.info)
   var threadLocalBarrier: PSym
   if barrier != nil:
     var varSection2 = newNodeI(nkVarSection, barrier.info)
-    threadLocalBarrier = addLocalVar(varSection2, nil, argsParam.owner,
+    threadLocalBarrier = addLocalVar(g, varSection2, nil, argsParam.owner,
                                      barrier.typ, barrier)
     body.add varSection2
-    body.add callCodegenProc("barrierEnter", threadLocalBarrier.newSymNode)
+    body.add callCodegenProc(g, "barrierEnter", threadLocalBarrier.newSymNode)
   var threadLocalProm: PSym
   if spawnKind == srByVar:
-    threadLocalProm = addLocalVar(varSection, nil, argsParam.owner, fv.typ, fv)
+    threadLocalProm = addLocalVar(g, varSection, nil, argsParam.owner, fv.typ, fv)
   elif fv != nil:
-    internalAssert fv.typ.kind == tyGenericInst
-    threadLocalProm = addLocalVar(varSection, nil, argsParam.owner, fv.typ, fv)
+    internalAssert g.config, fv.typ.kind == tyGenericInst
+    threadLocalProm = addLocalVar(g, varSection, nil, argsParam.owner, fv.typ, fv)
   body.add varSection
   body.add varInit
   if fv != nil and spawnKind != srByVar:
@@ -409,30 +412,30 @@ proc createWrapperProc(f: PNode; threadParam, argsParam: PSym;
     body.add newAsgnStmt(indirectAccess(threadLocalProm.newSymNode,
       "owner", fv.info), threadParam.newSymNode)
 
-  body.add callCodegenProc("nimArgsPassingDone", threadParam.newSymNode)
+  body.add callCodegenProc(g, "nimArgsPassingDone", threadParam.newSymNode)
   if spawnKind == srByVar:
     body.add newAsgnStmt(genDeref(threadLocalProm.newSymNode), call)
   elif fv != nil:
     let fk = fv.typ.sons[1].flowVarKind
     if fk == fvInvalid:
-      localError(f.info, "cannot create a flowVar of type: " &
+      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:
       let incRefCall = newNodeI(nkCall, fv.info, 2)
-      incRefCall.sons[0] = newSymNode(getSysMagic("GCref", mGCref))
+      incRefCall.sons[0] = newSymNode(getSysMagic(g, fv.info, "GCref", mGCref))
       incRefCall.sons[1] = indirectAccess(threadLocalProm.newSymNode,
                                           "data", fv.info)
       body.add incRefCall
     if barrier == nil:
       # by now 'fv' is shared and thus might have beeen overwritten! we need
       # to use the thread-local view instead:
-      body.add callCodegenProc("nimFlowVarSignal", threadLocalProm.newSymNode)
+      body.add callCodegenProc(g, "nimFlowVarSignal", threadLocalProm.newSymNode)
   else:
     body.add call
   if barrier != nil:
-    body.add callCodegenProc("barrierLeave", threadLocalBarrier.newSymNode)
+    body.add callCodegenProc(g, "barrierLeave", threadLocalBarrier.newSymNode)
 
   var params = newNodeI(nkFormalParams, f.info)
   params.add emptyNode
@@ -449,7 +452,8 @@ proc createWrapperProc(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(name), argsParam.owner, f.info,
+                  argsParam.options)
   result.ast = newProcNode(nkProcDef, f.info, body, params, newSymNode(result))
   result.typ = t
 
@@ -460,7 +464,7 @@ proc createCastExpr(argsParam: PSym; objType: PType): PNode =
   result.typ = newType(tyPtr, objType.owner)
   result.typ.rawAddSon(objType)
 
-proc setupArgsForConcurrency(n: PNode; objType: PType; scratchObj: PSym,
+proc setupArgsForConcurrency(g: ModuleGraph; n: PNode; objType: PType; scratchObj: PSym,
                              castExpr, call,
                              varSection, varInit, result: PNode) =
   let formals = n[0].typ.n
@@ -470,17 +474,17 @@ proc setupArgsForConcurrency(n: PNode; objType: PType; scratchObj: PSym,
     # 'tyOpenArray':
     var argType = n[i].typ.skipTypes(abstractInst)
     if i < formals.len and formals[i].typ.kind in {tyVar, tyLent}:
-      localError(n[i].info, "'spawn'ed function cannot have a 'var' parameter")
+      localError(g.config, n[i].info, "'spawn'ed function cannot have a 'var' parameter")
     #elif containsTyRef(argType):
     #  localError(n[i].info, "'spawn'ed function cannot refer to 'ref'/closure")
 
     let fieldname = if i < formals.len: formals[i].sym.name else: tmpName
-    var field = newSym(skField, fieldname, objType.owner, n.info)
+    var field = newSym(skField, fieldname, objType.owner, n.info, g.config.options)
     field.typ = argType
     objType.addField(field)
     result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n[i])
 
-    let temp = addLocalVar(varSection, varInit, objType.owner, argType,
+    let temp = addLocalVar(g, varSection, varInit, objType.owner, argType,
                            indirectAccess(castExpr, field, n.info))
     call.add(newSymNode(temp))
 
@@ -501,20 +505,20 @@ proc getRoot*(n: PNode): PSym =
     if getMagic(n) == mSlice: result = getRoot(n.sons[1])
   else: discard
 
-proc newIntLit*(value: BiggestInt): PNode =
+proc newIntLit*(g: ModuleGraph; info: TLineInfo; value: BiggestInt): PNode =
   result = nkIntLit.newIntNode(value)
-  result.typ = getSysType(tyInt)
+  result.typ = getSysType(g, info, tyInt)
 
-proc genHigh*(n: PNode): PNode =
+proc genHigh*(g: ModuleGraph; n: PNode): PNode =
   if skipTypes(n.typ, abstractVar).kind == tyArray:
-    result = newIntLit(lastOrd(skipTypes(n.typ, abstractVar)))
+    result = newIntLit(g, n.info, lastOrd(skipTypes(n.typ, abstractVar)))
   else:
     result = newNodeI(nkCall, n.info, 2)
-    result.typ = getSysType(tyInt)
-    result.sons[0] = newSymNode(getSysMagic("high", mHigh))
+    result.typ = getSysType(g, n.info, tyInt)
+    result.sons[0] = newSymNode(getSysMagic(g, n.info, "high", mHigh))
     result.sons[1] = n
 
-proc setupArgsForParallelism(n: PNode; objType: PType; scratchObj: PSym;
+proc setupArgsForParallelism(g: ModuleGraph; n: PNode; objType: PType; scratchObj: PSym;
                              castExpr, call,
                              varSection, varInit, result: PNode) =
   let formals = n[0].typ.n
@@ -529,16 +533,16 @@ proc setupArgsForParallelism(n: PNode; objType: PType; scratchObj: PSym;
     #  localError(n.info, "'spawn'ed function cannot refer to 'ref'/closure")
 
     let fieldname = if i < formals.len: formals[i].sym.name else: tmpName
-    var field = newSym(skField, fieldname, objType.owner, n.info)
+    var field = newSym(skField, fieldname, objType.owner, n.info, g.config.options)
 
     if argType.kind in {tyVarargs, tyOpenArray}:
       # important special case: we always create a zero-copy slice:
       let slice = newNodeI(nkCall, n.info, 4)
       slice.typ = n.typ
-      slice.sons[0] = newSymNode(createMagic("slice", mSlice))
-      slice.sons[0].typ = getSysType(tyInt) # fake type
-      var fieldB = newSym(skField, tmpName, objType.owner, n.info)
-      fieldB.typ = getSysType(tyInt)
+      slice.sons[0] = newSymNode(createMagic(g, "slice", mSlice))
+      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)
 
       if getMagic(n) == mSlice:
@@ -547,13 +551,13 @@ proc setupArgsForParallelism(n: PNode; objType: PType; scratchObj: PSym;
         objType.addField(field)
         result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a)
 
-        var fieldA = newSym(skField, tmpName, objType.owner, n.info)
-        fieldA.typ = getSysType(tyInt)
+        var fieldA = newSym(skField, tmpName, objType.owner, n.info, g.config.options)
+        fieldA.typ = getSysType(g, n.info, tyInt)
         objType.addField(fieldA)
         result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldA), n[2])
         result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldB), n[3])
 
-        let threadLocal = addLocalVar(varSection,nil, objType.owner, fieldA.typ,
+        let threadLocal = addLocalVar(g, varSection,nil, objType.owner, fieldA.typ,
                                       indirectAccess(castExpr, fieldA, n.info),
                                       useShallowCopy=true)
         slice.sons[2] = threadLocal.newSymNode
@@ -562,13 +566,13 @@ proc setupArgsForParallelism(n: PNode; objType: PType; scratchObj: PSym;
         field.typ = a.typ
         objType.addField(field)
         result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a)
-        result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldB), genHigh(n))
+        result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldB), genHigh(g, n))
 
-        slice.sons[2] = newIntLit(0)
+        slice.sons[2] = newIntLit(g, n.info, 0)
       # the array itself does not need to go through a thread local variable:
       slice.sons[1] = genDeref(indirectAccess(castExpr, field, n.info))
 
-      let threadLocal = addLocalVar(varSection,nil, objType.owner, fieldB.typ,
+      let threadLocal = addLocalVar(g, varSection,nil, objType.owner, fieldB.typ,
                                     indirectAccess(castExpr, fieldB, n.info),
                                     useShallowCopy=true)
       slice.sons[3] = threadLocal.newSymNode
@@ -580,7 +584,7 @@ proc setupArgsForParallelism(n: PNode; objType: PType; scratchObj: PSym;
       field.typ = a.typ
       objType.addField(field)
       result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a)
-      let threadLocal = addLocalVar(varSection,nil, objType.owner, field.typ,
+      let threadLocal = addLocalVar(g, varSection,nil, objType.owner, field.typ,
                                     indirectAccess(castExpr, field, n.info),
                                     useShallowCopy=true)
       call.add(genDeref(threadLocal.newSymNode))
@@ -589,13 +593,13 @@ proc setupArgsForParallelism(n: PNode; objType: PType; scratchObj: PSym;
       field.typ = argType
       objType.addField(field)
       result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n)
-      let threadLocal = addLocalVar(varSection, varInit,
+      let threadLocal = addLocalVar(g, varSection, varInit,
                                     objType.owner, field.typ,
                                     indirectAccess(castExpr, field, n.info),
                                     useShallowCopy=true)
       call.add(threadLocal.newSymNode)
 
-proc wrapProcForSpawn*(owner: PSym; spawnExpr: PNode; retType: PType;
+proc wrapProcForSpawn*(g: ModuleGraph; owner: PSym; spawnExpr: PNode; retType: PType;
                        barrier, dest: PNode = nil): PNode =
   # if 'barrier' != nil, then it is in a 'parallel' section and we
   # generate quite different code
@@ -603,35 +607,35 @@ proc wrapProcForSpawn*(owner: PSym; spawnExpr: PNode; retType: PType;
   let spawnKind = spawnResult(retType, barrier!=nil)
   case spawnKind
   of srVoid:
-    internalAssert dest == nil
+    internalAssert g.config, dest == nil
     result = newNodeI(nkStmtList, n.info)
   of srFlowVar:
-    internalAssert dest == nil
+    internalAssert g.config, dest == nil
     result = newNodeIT(nkStmtListExpr, n.info, retType)
   of srByVar:
-    if dest == nil: localError(n.info, "'spawn' must not be discarded")
+    if dest == nil: localError(g.config, n.info, "'spawn' must not be discarded")
     result = newNodeI(nkStmtList, n.info)
 
   if n.kind notin nkCallKinds:
-    localError(n.info, "'spawn' takes a call expression")
+    localError(g.config, n.info, "'spawn' takes a call expression")
     return
-  if optThreadAnalysis in gGlobalOptions:
+  if optThreadAnalysis in g.config.globalOptions:
     if {tfThread, tfNoSideEffect} * n[0].typ.flags == {}:
-      localError(n.info, "'spawn' takes a GC safe call expression")
+      localError(g.config, n.info, "'spawn' takes a GC safe call expression")
   var
-    threadParam = newSym(skParam, getIdent"thread", owner, n.info)
-    argsParam = newSym(skParam, getIdent"args", owner, n.info)
+    threadParam = newSym(skParam, getIdent"thread", owner, n.info, g.config.options)
+    argsParam = newSym(skParam, getIdent"args", owner, n.info, g.config.options)
   block:
-    let ptrType = getSysType(tyPointer)
+    let ptrType = getSysType(g, n.info, tyPointer)
     threadParam.typ = ptrType
     argsParam.typ = ptrType
     argsParam.position = 1
 
-  var objType = createObj(owner, n.info)
+  var objType = createObj(g, owner, n.info)
   incl(objType.flags, tfFinal)
   let castExpr = createCastExpr(argsParam, objType)
 
-  var scratchObj = newSym(skVar, getIdent"scratch", owner, n.info)
+  var scratchObj = newSym(skVar, getIdent"scratch", owner, n.info, g.config.options)
   block:
     scratchObj.typ = objType
     incl(scratchObj.flags, sfFromGeneric)
@@ -644,36 +648,36 @@ proc wrapProcForSpawn*(owner: PSym; spawnExpr: PNode; retType: PType;
   # templates and macros are in fact valid here due to the nature of
   # the transformation:
   if fn.kind == nkClosure:
-    localError(n.info, "closure in spawn environment is not allowed")
+    localError(g.config, n.info, "closure in spawn environment is not allowed")
   if not (fn.kind == nkSym and fn.sym.kind in {skProc, skTemplate, skMacro,
                                                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)
+    var field = newSym(skField, getIdent"fn", owner, n.info, g.config.options)
     field.typ = argType
     objType.addField(field)
     result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n[0])
     fn = indirectAccess(castExpr, field, n.info)
   elif fn.kind == nkSym and fn.sym.kind == skIterator:
-    localError(n.info, "iterator in spawn environment is not allowed")
+    localError(g.config, n.info, "iterator in spawn environment is not allowed")
   elif fn.typ.callConv == ccClosure:
-    localError(n.info, "closure in spawn environment is not allowed")
+    localError(g.config, n.info, "closure in spawn environment is not allowed")
 
   call.add(fn)
   var varSection = newNodeI(nkVarSection, n.info)
   var varInit = newNodeI(nkStmtList, n.info)
   if barrier.isNil:
-    setupArgsForConcurrency(n, objType, scratchObj, castExpr, call,
+    setupArgsForConcurrency(g, n, objType, scratchObj, castExpr, call,
                             varSection, varInit, result)
   else:
-    setupArgsForParallelism(n, objType, scratchObj, castExpr, call,
+    setupArgsForParallelism(g, n, objType, scratchObj, castExpr, call,
                             varSection, varInit, result)
 
   var barrierAsExpr: PNode = nil
   if barrier != nil:
     let typ = newType(tyPtr, owner)
-    typ.rawAddSon(magicsys.getCompilerProc("Barrier").typ)
-    var field = newSym(skField, getIdent"barrier", owner, n.info)
+    typ.rawAddSon(magicsys.getCompilerProc(g, "Barrier").typ)
+    var field = newSym(skField, getIdent"barrier", owner, n.info, g.config.options)
     field.typ = typ
     objType.addField(field)
     result.add newFastAsgnStmt(newDotExpr(scratchObj, field), barrier)
@@ -681,7 +685,7 @@ proc wrapProcForSpawn*(owner: PSym; spawnExpr: PNode; retType: PType;
 
   var fvField, fvAsExpr: PNode = nil
   if spawnKind == srFlowVar:
-    var field = newSym(skField, getIdent"fv", owner, n.info)
+    var field = newSym(skField, getIdent"fv", owner, n.info, g.config.options)
     field.typ = retType
     objType.addField(field)
     fvField = newDotExpr(scratchObj, field)
@@ -689,20 +693,20 @@ proc wrapProcForSpawn*(owner: PSym; spawnExpr: PNode; retType: PType;
     # create flowVar:
     result.add newFastAsgnStmt(fvField, callProc(spawnExpr[^1]))
     if barrier == nil:
-      result.add callCodegenProc("nimFlowVarCreateSemaphore", fvField)
+      result.add callCodegenProc(g, "nimFlowVarCreateSemaphore", fvField)
 
   elif spawnKind == srByVar:
-    var field = newSym(skField, getIdent"fv", owner, n.info)
+    var field = newSym(skField, getIdent"fv", owner, n.info, g.config.options)
     field.typ = newType(tyPtr, objType.owner)
     field.typ.rawAddSon(retType)
     objType.addField(field)
     fvAsExpr = indirectAccess(castExpr, field, n.info)
     result.add newFastAsgnStmt(newDotExpr(scratchObj, field), genAddrOf(dest))
 
-  let wrapper = createWrapperProc(fn, threadParam, argsParam,
+  let wrapper = createWrapperProc(g, fn, threadParam, argsParam,
                                   varSection, varInit, call,
                                   barrierAsExpr, fvAsExpr, spawnKind)
-  result.add callCodegenProc("nimSpawn" & $spawnExpr.len, wrapper.newSymNode,
+  result.add callCodegenProc(g, "nimSpawn" & $spawnExpr.len, wrapper.newSymNode,
                              genAddrOf(scratchObj.newSymNode), nil, spawnExpr)
 
   if spawnKind == srFlowVar: result.add fvField
diff --git a/compiler/magicsys.nim b/compiler/magicsys.nim
index 6a9d69082..b5577d961 100644
--- a/compiler/magicsys.nim
+++ b/compiler/magicsys.nim
@@ -10,63 +10,62 @@
 # Built-in types and compilerprocs are registered here.
 
 import
-  ast, astalgo, hashes, msgs, platform, nversion, times, idents, rodread
+  ast, astalgo, hashes, msgs, platform, nversion, times, idents, rodread,
+  modulegraphs
 
-var systemModule*: PSym
+export createMagic
 
-var
-  gSysTypes: array[TTypeKind, PType]
-  compilerprocs: TStrTable
-  exposed: TStrTable
+proc nilOrSysInt*(g: ModuleGraph): PType = g.sysTypes[tyInt]
 
-proc nilOrSysInt*: PType = gSysTypes[tyInt]
+proc registerSysType*(g: ModuleGraph; t: PType) =
+  if g.sysTypes[t.kind] == nil: g.sysTypes[t.kind] = t
 
-proc registerSysType*(t: PType) =
-  if gSysTypes[t.kind] == nil: gSysTypes[t.kind] = t
-
-proc newSysType(kind: TTypeKind, size: int): PType =
-  result = newType(kind, systemModule)
+proc newSysType(g: ModuleGraph; kind: TTypeKind, size: int): PType =
+  result = newType(kind, g.systemModule)
   result.size = size
   result.align = size.int16
 
-proc getSysSym*(name: string): PSym =
-  result = strTableGet(systemModule.tab, getIdent(name))
+proc getSysSym*(g: ModuleGraph; info: TLineInfo; name: string): PSym =
+  result = strTableGet(g.systemModule.tab, getIdent(name))
   if result == nil:
-    rawMessage(errSystemNeeds, name)
-    result = newSym(skError, getIdent(name), systemModule, systemModule.info)
-    result.typ = newType(tyError, systemModule)
+    localError(g.config, info, "system module needs: " & name)
+    result = newSym(skError, getIdent(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
 
-proc createMagic*(name: string, m: TMagic): PSym =
-  result = newSym(skProc, getIdent(name), nil, unknownLineInfo())
-  result.magic = m
+when false:
+  proc createMagic*(g: ModuleGraph; name: string, m: TMagic): PSym =
+    result = newSym(skProc, getIdent(name), nil, unknownLineInfo())
+    result.magic = m
 
-let
-  opNot* = createMagic("not", mNot)
-  opContains* = createMagic("contains", mInSet)
+when false:
+  let
+    opNot* = createMagic("not", mNot)
+    opContains* = createMagic("contains", mInSet)
 
-proc getSysMagic*(name: string, m: TMagic): PSym =
+proc getSysMagic*(g: ModuleGraph; info: TLineInfo; name: string, m: TMagic): PSym =
   var ti: TIdentIter
   let id = getIdent(name)
-  var r = initIdentIter(ti, systemModule.tab, id)
+  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
       result = r
-    r = nextIdentIter(ti, systemModule.tab)
+    r = nextIdentIter(ti, g.systemModule.tab)
   if result != nil: return result
-  rawMessage(errSystemNeeds, name)
-  result = newSym(skError, id, systemModule, systemModule.info)
-  result.typ = newType(tyError, systemModule)
+  localError(g.config, info, "system module needs: " & name)
+  result = newSym(skError, id, g.systemModule, g.systemModule.info, {})
+  result.typ = newType(tyError, g.systemModule)
 
-proc sysTypeFromName*(name: string): PType =
-  result = getSysSym(name).typ
+proc sysTypeFromName*(g: ModuleGraph; info: TLineInfo; name: string): PType =
+  result = getSysSym(g, info, name).typ
 
-proc getSysType*(kind: TTypeKind): PType =
-  result = gSysTypes[kind]
+proc getSysType*(g: ModuleGraph; info: TLineInfo; kind: TTypeKind): PType =
+  template sysTypeFromName(s: string): untyped = sysTypeFromName(g, info, s)
+  result = g.sysTypes[kind]
   if result == nil:
     case kind
     of tyInt: result = sysTypeFromName("int")
@@ -88,51 +87,49 @@ proc getSysType*(kind: TTypeKind): PType =
     of tyString: result = sysTypeFromName("string")
     of tyCString: result = sysTypeFromName("cstring")
     of tyPointer: result = sysTypeFromName("pointer")
-    of tyNil: result = newSysType(tyNil, ptrSize)
-    else: internalError("request for typekind: " & $kind)
-    gSysTypes[kind] = result
+    of tyNil: result = newSysType(g, tyNil, ptrSize)
+    else: internalError(g.config, "request for typekind: " & $kind)
+    g.sysTypes[kind] = result
   if result.kind != kind:
-    internalError("wanted: " & $kind & " got: " & $result.kind)
-  if result == nil: internalError("type not found: " & $kind)
-
-var
-  intTypeCache: array[-5..64, PType]
+    internalError(g.config, "wanted: " & $kind & " got: " & $result.kind)
+  if result == nil: internalError(g.config, "type not found: " & $kind)
 
-proc resetSysTypes* =
-  systemModule = nil
-  initStrTable(compilerprocs)
-  initStrTable(exposed)
-  for i in low(gSysTypes)..high(gSysTypes):
-    gSysTypes[i] = nil
+proc resetSysTypes*(g: ModuleGraph) =
+  g.systemModule = nil
+  initStrTable(g.compilerprocs)
+  initStrTable(g.exposed)
+  for i in low(g.sysTypes)..high(g.sysTypes):
+    g.sysTypes[i] = nil
 
-  for i in low(intTypeCache)..high(intTypeCache):
-    intTypeCache[i] = nil
+  for i in low(g.intTypeCache)..high(g.intTypeCache):
+    g.intTypeCache[i] = nil
 
-proc getIntLitType*(literal: PNode): PType =
+proc getIntLitType*(g: ModuleGraph; literal: PNode): PType =
   # we cache some common integer literal types for performance:
   let value = literal.intVal
-  if value >= low(intTypeCache) and value <= high(intTypeCache):
-    result = intTypeCache[value.int]
+  if value >= low(g.intTypeCache) and value <= high(g.intTypeCache):
+    result = g.intTypeCache[value.int]
     if result == nil:
-      let ti = getSysType(tyInt)
+      let ti = getSysType(g, literal.info, tyInt)
       result = copyType(ti, ti.owner, false)
       result.n = literal
-      intTypeCache[value.int] = result
+      g.intTypeCache[value.int] = result
   else:
-    let ti = getSysType(tyInt)
+    let ti = getSysType(g, literal.info, tyInt)
     result = copyType(ti, ti.owner, false)
     result.n = literal
 
-proc getFloatLitType*(literal: PNode): PType =
+proc getFloatLitType*(g: ModuleGraph; literal: PNode): PType =
   # for now we do not cache these:
-  result = newSysType(tyFloat, size=8)
+  result = newSysType(g, tyFloat, size=8)
   result.n = literal
 
 proc skipIntLit*(t: PType): PType {.inline.} =
-  if t.n != nil:
-    if t.kind in {tyInt, tyFloat}:
-      return getSysType(t.kind)
-  result = t
+  if t.n != nil and t.kind in {tyInt, tyFloat}:
+    result = copyType(t, t.owner, false)
+    result.n = nil
+  else:
+    result = t
 
 proc addSonSkipIntLit*(father, son: PType) =
   if isNil(father.sons): father.sons = @[]
@@ -140,60 +137,59 @@ proc addSonSkipIntLit*(father, son: PType) =
   add(father.sons, s)
   propagateToOwner(father, s)
 
-proc setIntLitType*(result: PNode) =
+proc setIntLitType*(g: ModuleGraph; result: PNode) =
   let i = result.intVal
   case platform.intSize
-  of 8: result.typ = getIntLitType(result)
+  of 8: result.typ = getIntLitType(g, result)
   of 4:
     if i >= low(int32) and i <= high(int32):
-      result.typ = getIntLitType(result)
+      result.typ = getIntLitType(g, result)
     else:
-      result.typ = getSysType(tyInt64)
+      result.typ = getSysType(g, result.info, tyInt64)
   of 2:
     if i >= low(int16) and i <= high(int16):
-      result.typ = getIntLitType(result)
+      result.typ = getIntLitType(g, result)
     elif i >= low(int32) and i <= high(int32):
-      result.typ = getSysType(tyInt32)
+      result.typ = getSysType(g, result.info, tyInt32)
     else:
-      result.typ = getSysType(tyInt64)
+      result.typ = getSysType(g, result.info, tyInt64)
   of 1:
     # 8 bit CPUs are insane ...
     if i >= low(int8) and i <= high(int8):
-      result.typ = getIntLitType(result)
+      result.typ = getIntLitType(g, result)
     elif i >= low(int16) and i <= high(int16):
-      result.typ = getSysType(tyInt16)
+      result.typ = getSysType(g, result.info, tyInt16)
     elif i >= low(int32) and i <= high(int32):
-      result.typ = getSysType(tyInt32)
+      result.typ = getSysType(g, result.info, tyInt32)
     else:
-      result.typ = getSysType(tyInt64)
-  else: internalError(result.info, "invalid int size")
+      result.typ = getSysType(g, result.info, tyInt64)
+  else:
+    internalError(g.config, result.info, "invalid int size")
 
-proc getCompilerProc*(name: string): PSym =
+proc getCompilerProc*(g: ModuleGraph; name: string): PSym =
   let ident = getIdent(name)
-  result = strTableGet(compilerprocs, ident)
-  if result == nil:
-    result = strTableGet(rodCompilerprocs, ident)
-    if result != nil:
-      strTableAdd(compilerprocs, result)
-      if result.kind == skStub: loadStub(result)
-      if result.kind == skAlias: result = result.owner
+  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*(s: PSym) =
-  strTableAdd(compilerprocs, s)
+proc registerCompilerProc*(g: ModuleGraph; s: PSym) =
+  strTableAdd(g.compilerprocs, s)
 
-proc registerNimScriptSymbol*(s: PSym) =
+proc registerNimScriptSymbol*(g: ModuleGraph; s: PSym) =
   # Nimscript symbols must be al unique:
-  let conflict = strTableGet(exposed, s.name)
+  let conflict = strTableGet(g.exposed, s.name)
   if conflict == nil:
-    strTableAdd(exposed, s)
+    strTableAdd(g.exposed, s)
   else:
-    localError(s.info, "symbol conflicts with other .exportNims symbol at: " &
-      $conflict.info)
-
-proc getNimScriptSymbol*(name: string): PSym =
-  strTableGet(exposed, getIdent(name))
+    localError(g.config, s.info,
+      "symbol conflicts with other .exportNims symbol at: " & $conflict.info)
 
-proc resetNimScriptSymbols*() = initStrTable(exposed)
+proc getNimScriptSymbol*(g: ModuleGraph; name: string): PSym =
+  strTableGet(g.exposed, getIdent(name))
 
-initStrTable(compilerprocs)
-initStrTable(exposed)
+proc resetNimScriptSymbols*(g: ModuleGraph) = initStrTable(g.exposed)
diff --git a/compiler/main.nim b/compiler/main.nim
index 20a715886..e3f00db9e 100644
--- a/compiler/main.nim
+++ b/compiler/main.nim
@@ -16,12 +16,12 @@ import
   cgen, jsgen, json, nversion,
   platform, nimconf, importer, passaux, depends, vm, vmdef, types, idgen,
   docgen2, service, parser, modules, ccgutils, sigmatch, ropes,
-  modulegraphs, tables, rod
+  modulegraphs, tables, rod, configuration
 
-from magicsys import systemModule, resetSysTypes
+from magicsys import resetSysTypes
 
-proc rodPass =
-  if gSymbolFiles in {enabledSf, writeOnlySf}:
+proc rodPass(g: ModuleGraph) =
+  if g.config.symbolFiles in {enabledSf, writeOnlySf}:
     registerPass(rodwritePass)
 
 proc codegenPass =
@@ -35,7 +35,7 @@ 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.int32))
+      f.writeLine(toFullPath(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)
@@ -46,55 +46,55 @@ proc commandGenDepend(graph: ModuleGraph; cache: IdentCache) =
   registerPass(gendependPass)
   #registerPass(cleanupPass)
   compileProject(graph, cache)
-  writeDepsFile(graph, gProjectFull)
-  generateDot(gProjectFull)
-  execExternalProgram("dot -Tpng -o" & changeFileExt(gProjectFull, "png") &
-      ' ' & changeFileExt(gProjectFull, "dot"))
+  let project = graph.config.projectFull
+  writeDepsFile(graph, project)
+  generateDot(project)
+  execExternalProgram(graph.config, "dot -Tpng -o" & changeFileExt(project, "png") &
+      ' ' & changeFileExt(project, "dot"))
 
 proc commandCheck(graph: ModuleGraph; cache: IdentCache) =
-  msgs.gErrorMax = high(int)  # do not stop after first error
-  defineSymbol("nimcheck")
+  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()
+  rodPass(graph)
   compileProject(graph, cache)
 
 proc commandDoc2(graph: ModuleGraph; cache: IdentCache; json: bool) =
-  msgs.gErrorMax = high(int)  # do not stop after first error
+  graph.config.errorMax = high(int)  # do not stop after first error
   semanticPasses()
   if json: registerPass(docgen2JsonPass)
   else: registerPass(docgen2Pass)
   #registerPass(cleanupPass())
   compileProject(graph, cache)
-  finishDoc2Pass(gProjectName)
+  finishDoc2Pass(graph.config.projectName)
 
 proc commandCompileToC(graph: ModuleGraph; cache: IdentCache) =
-  extccomp.initVars()
+  let conf = graph.config
+  extccomp.initVars(conf)
   semanticPasses()
   registerPass(cgenPass)
-  rodPass()
+  rodPass(graph)
   #registerPass(cleanupPass())
 
   compileProject(graph, cache)
-  cgenWriteModules(graph.backend, graph.config)
-  if gCmd != cmdRun:
-    let proj = changeFileExt(gProjectFull, "")
-    extccomp.callCCompiler(proj)
-    extccomp.writeJsonBuildInstructions(proj)
-    if optGenScript in gGlobalOptions:
-      writeDepsFile(graph, toGeneratedFile(proj, ""))
+  cgenWriteModules(graph.backend, conf)
+  if conf.cmd != cmdRun:
+    let proj = changeFileExt(conf.projectFull, "")
+    extccomp.callCCompiler(conf, proj)
+    extccomp.writeJsonBuildInstructions(conf, proj)
+    if optGenScript in graph.config.globalOptions:
+      writeDepsFile(graph, toGeneratedFile(conf, proj, ""))
 
 proc commandJsonScript(graph: ModuleGraph; cache: IdentCache) =
-  let proj = changeFileExt(gProjectFull, "")
-  extccomp.runJsonBuildInstructions(proj)
+  let proj = changeFileExt(graph.config.projectFull, "")
+  extccomp.runJsonBuildInstructions(graph.config, proj)
 
 proc commandCompileToJS(graph: ModuleGraph; cache: IdentCache) =
   #incl(gGlobalOptions, optSafeCode)
   setTarget(osJS, cpuJS)
   #initDefines()
-  defineSymbol("nimrod") # 'nimrod' is always defined
-  defineSymbol("ecmascript") # For backward compatibility
-  defineSymbol("js")
-  if gCmd == cmdCompileToPHP: defineSymbol("nimphp")
+  defineSymbol(graph.config.symbols, "ecmascript") # For backward compatibility
+  defineSymbol(graph.config.symbols, "js")
   semanticPasses()
   registerPass(JSgenPass)
   compileProject(graph, cache)
@@ -102,19 +102,19 @@ proc commandCompileToJS(graph: ModuleGraph; cache: IdentCache) =
 proc interactivePasses(graph: ModuleGraph; cache: IdentCache) =
   #incl(gGlobalOptions, optSafeCode)
   #setTarget(osNimrodVM, cpuNimrodVM)
-  initDefines()
-  defineSymbol("nimscript")
-  when hasFFI: defineSymbol("nimffi")
+  initDefines(graph.config.symbols)
+  defineSymbol(graph.config.symbols, "nimscript")
+  when hasFFI: defineSymbol(graph.config.symbols, "nimffi")
   registerPass(verbosePass)
   registerPass(semPass)
   registerPass(evalPass)
 
 proc commandInteractive(graph: ModuleGraph; cache: IdentCache) =
-  msgs.gErrorMax = high(int)  # do not stop after first error
+  graph.config.errorMax = high(int)  # do not stop after first error
   interactivePasses(graph, cache)
   compileSystemModule(graph, cache)
-  if commandArgs.len > 0:
-    discard graph.compileModule(fileInfoIdx(gProjectFull), cache, {})
+  if graph.config.commandArgs.len > 0:
+    discard graph.compileModule(fileInfoIdx(graph.config, graph.config.projectFull), cache, {})
   else:
     var m = graph.makeStdinModule()
     incl(m.flags, sfMainModule)
@@ -126,179 +126,171 @@ proc evalNim(graph: ModuleGraph; nodes: PNode, module: PSym; cache: IdentCache)
   carryPasses(graph, nodes, module, cache, evalPasses)
 
 proc commandEval(graph: ModuleGraph; cache: IdentCache; exp: string) =
-  if systemModule == nil:
+  if graph.systemModule == nil:
     interactivePasses(graph, cache)
     compileSystemModule(graph, cache)
   let echoExp = "echo \"eval\\t\", " & "repr(" & exp & ")"
-  evalNim(graph, echoExp.parseString(cache), makeStdinModule(graph), cache)
+  evalNim(graph, echoExp.parseString(cache, graph.config),
+    makeStdinModule(graph), cache)
 
-proc commandScan(cache: IdentCache) =
-  var f = addFileExt(mainCommandArg(), NimExt)
+proc commandScan(cache: IdentCache, config: ConfigRef) =
+  var f = addFileExt(mainCommandArg(config), NimExt)
   var stream = llStreamOpen(f, fmRead)
   if stream != nil:
     var
       L: TLexer
       tok: TToken
     initToken(tok)
-    openLexer(L, f, stream, cache)
+    openLexer(L, f, stream, cache, config)
     while true:
       rawGetTok(L, tok)
-      printTok(tok)
+      printTok(config, tok)
       if tok.tokType == tkEof: break
     closeLexer(L)
   else:
-    rawMessage(errCannotOpenFile, f)
+    rawMessage(config, errGenerated, "cannot open file: " & f)
 
 const
-  SimulateCaasMemReset = false
   PrintRopeCacheStats = false
 
 proc mainCommand*(graph: ModuleGraph; cache: IdentCache) =
-  when SimulateCaasMemReset:
-    gGlobalOptions.incl(optCaasEnabled)
+  let conf = graph.config
 
   setupModuleCache()
   # In "nim serve" scenario, each command must reset the registered passes
   clearPasses()
-  gLastCmdTime = epochTime()
-  searchPaths.add(options.libpath)
-  when false: # gProjectFull.len != 0:
-    # current path is always looked first for modules
-    prependStr(searchPaths, gProjectPath)
+  conf.lastCmdTime = epochTime()
+  conf.searchPaths.add(conf.libpath)
   setId(100)
-  case command.normalize
+  case conf.command.normalize
   of "c", "cc", "compile", "compiletoc":
     # compile means compileToC currently
-    gCmd = cmdCompileToC
+    conf.cmd = cmdCompileToC
     commandCompileToC(graph, cache)
   of "cpp", "compiletocpp":
-    gCmd = cmdCompileToCpp
-    defineSymbol("cpp")
+    conf.cmd = cmdCompileToCpp
+    defineSymbol(graph.config.symbols, "cpp")
     commandCompileToC(graph, cache)
   of "objc", "compiletooc":
-    gCmd = cmdCompileToOC
-    defineSymbol("objc")
+    conf.cmd = cmdCompileToOC
+    defineSymbol(graph.config.symbols, "objc")
     commandCompileToC(graph, cache)
   of "run":
-    gCmd = cmdRun
+    conf.cmd = cmdRun
     when hasTinyCBackend:
       extccomp.setCC("tcc")
       commandCompileToC(graph, cache)
     else:
-      rawMessage(errInvalidCommandX, command)
+      rawMessage(conf, errGenerated, "'run' command not available; rebuild with -d:tinyc")
   of "js", "compiletojs":
-    gCmd = cmdCompileToJS
-    commandCompileToJS(graph, cache)
-  of "php":
-    gCmd = cmdCompileToPHP
+    conf.cmd = cmdCompileToJS
     commandCompileToJS(graph, cache)
   of "doc0":
-    wantMainModule()
-    gCmd = cmdDoc
-    loadConfigs(DocConfig, cache)
-    commandDoc()
+    wantMainModule(conf)
+    conf.cmd = cmdDoc
+    loadConfigs(DocConfig, cache, conf)
+    commandDoc(conf)
   of "doc2", "doc":
-    gCmd = cmdDoc
-    loadConfigs(DocConfig, cache)
-    defineSymbol("nimdoc")
+    conf.cmd = cmdDoc
+    loadConfigs(DocConfig, cache, conf)
+    defineSymbol(conf.symbols, "nimdoc")
     commandDoc2(graph, cache, false)
   of "rst2html":
-    gCmd = cmdRst2html
-    loadConfigs(DocConfig, cache)
-    commandRst2Html()
+    conf.cmd = cmdRst2html
+    loadConfigs(DocConfig, cache, conf)
+    commandRst2Html(conf)
   of "rst2tex":
-    gCmd = cmdRst2tex
-    loadConfigs(DocTexConfig, cache)
-    commandRst2TeX()
+    conf.cmd = cmdRst2tex
+    loadConfigs(DocTexConfig, cache, conf)
+    commandRst2TeX(conf)
   of "jsondoc0":
-    wantMainModule()
-    gCmd = cmdDoc
-    loadConfigs(DocConfig, cache)
-    wantMainModule()
-    defineSymbol("nimdoc")
-    commandJson()
+    wantMainModule(conf)
+    conf.cmd = cmdDoc
+    loadConfigs(DocConfig, cache, conf)
+    wantMainModule(conf)
+    defineSymbol(conf.symbols, "nimdoc")
+    commandJson(conf)
   of "jsondoc2", "jsondoc":
-    gCmd = cmdDoc
-    loadConfigs(DocConfig, cache)
-    wantMainModule()
-    defineSymbol("nimdoc")
+    conf.cmd = cmdDoc
+    loadConfigs(DocConfig, cache, conf)
+    wantMainModule(conf)
+    defineSymbol(conf.symbols, "nimdoc")
     commandDoc2(graph, cache, true)
   of "ctags":
-    wantMainModule()
-    gCmd = cmdDoc
-    loadConfigs(DocConfig, cache)
-    wantMainModule()
-    defineSymbol("nimdoc")
-    commandTags()
+    wantMainModule(conf)
+    conf.cmd = cmdDoc
+    loadConfigs(DocConfig, cache, conf)
+    defineSymbol(conf.symbols, "nimdoc")
+    commandTags(conf)
   of "buildindex":
-    gCmd = cmdDoc
-    loadConfigs(DocConfig, cache)
-    commandBuildIndex()
+    conf.cmd = cmdDoc
+    loadConfigs(DocConfig, cache, conf)
+    commandBuildIndex(conf)
   of "gendepend":
-    gCmd = cmdGenDepend
+    conf.cmd = cmdGenDepend
     commandGenDepend(graph, cache)
   of "dump":
-    gCmd = cmdDump
-    if getConfigVar("dump.format") == "json":
-      wantMainModule()
+    conf.cmd = cmdDump
+    if getConfigVar(conf, "dump.format") == "json":
+      wantMainModule(conf)
 
       var definedSymbols = newJArray()
-      for s in definedSymbolNames(): definedSymbols.elems.add(%s)
+      for s in definedSymbolNames(conf.symbols): definedSymbols.elems.add(%s)
 
       var libpaths = newJArray()
-      for dir in searchPaths: libpaths.elems.add(%dir)
+      for dir in conf.searchPaths: libpaths.elems.add(%dir)
 
       var dumpdata = % [
         (key: "version", val: %VersionAsString),
-        (key: "project_path", val: %gProjectFull),
+        (key: "project_path", val: %conf.projectFull),
         (key: "defined_symbols", val: definedSymbols),
         (key: "lib_paths", val: libpaths)
       ]
 
-      msgWriteln($dumpdata, {msgStdout, msgSkipHook})
+      msgWriteln(conf, $dumpdata, {msgStdout, msgSkipHook})
     else:
-      msgWriteln("-- list of currently defined symbols --",
+      msgWriteln(conf, "-- list of currently defined symbols --",
                  {msgStdout, msgSkipHook})
-      for s in definedSymbolNames(): msgWriteln(s, {msgStdout, msgSkipHook})
-      msgWriteln("-- end of list --", {msgStdout, msgSkipHook})
+      for s in definedSymbolNames(conf.symbols): msgWriteln(conf, s, {msgStdout, msgSkipHook})
+      msgWriteln(conf, "-- end of list --", {msgStdout, msgSkipHook})
 
-      for it in searchPaths: msgWriteln(it)
+      for it in conf.searchPaths: msgWriteln(conf, it)
   of "check":
-    gCmd = cmdCheck
+    conf.cmd = cmdCheck
     commandCheck(graph, cache)
   of "parse":
-    gCmd = cmdParse
-    wantMainModule()
-    discard parseFile(gProjectMainIdx, cache)
+    conf.cmd = cmdParse
+    wantMainModule(conf)
+    discard parseFile(FileIndex conf.projectMainIdx, cache, conf)
   of "scan":
-    gCmd = cmdScan
-    wantMainModule()
-    commandScan(cache)
-    msgWriteln("Beware: Indentation tokens depend on the parser's state!")
+    conf.cmd = cmdScan
+    wantMainModule(conf)
+    commandScan(cache, conf)
+    msgWriteln(conf, "Beware: Indentation tokens depend on the parser's state!")
   of "secret":
-    gCmd = cmdInteractive
+    conf.cmd = cmdInteractive
     commandInteractive(graph, cache)
   of "e":
-    commandEval(graph, cache, mainCommandArg())
+    commandEval(graph, cache, mainCommandArg(conf))
   of "nop", "help":
     # prevent the "success" message:
-    gCmd = cmdDump
+    conf.cmd = cmdDump
   of "jsonscript":
-    gCmd = cmdJsonScript
+    conf.cmd = cmdJsonScript
     commandJsonScript(graph, cache)
   else:
-    rawMessage(errInvalidCommandX, command)
+    rawMessage(conf, errGenerated, "invalid command: " & conf.command)
 
-  if msgs.gErrorCounter == 0 and
-     gCmd notin {cmdInterpret, cmdRun, cmdDump}:
+  if conf.errorCounter == 0 and
+     conf.cmd notin {cmdInterpret, cmdRun, cmdDump}:
     when declared(system.getMaxMem):
       let usedMem = formatSize(getMaxMem()) & " peakmem"
     else:
       let usedMem = formatSize(getTotalMem())
-    rawMessage(hintSuccessX, [$gLinesCompiled,
-               formatFloat(epochTime() - gLastCmdTime, ffDecimal, 3),
+    rawMessage(conf, hintSuccessX, [$conf.linesCompiled,
+               formatFloat(epochTime() - conf.lastCmdTime, ffDecimal, 3),
                usedMem,
-               if condSyms.isDefined("release"): "Release Build"
+               if isDefined(conf, "release"): "Release Build"
                else: "Debug Build"])
 
   when PrintRopeCacheStats:
@@ -309,9 +301,6 @@ proc mainCommand*(graph: ModuleGraph; cache: IdentCache) =
     echo "  efficiency: ", formatFloat(1-(gCacheMisses.float/gCacheTries.float),
                                        ffDecimal, 3)
 
-  when SimulateCaasMemReset:
-    resetMemory()
-
-  resetAttributes()
+  resetAttributes(conf)
 
-proc mainCommand*() = mainCommand(newModuleGraph(newConfigRef()), newIdentCache())
+#proc mainCommand*() = mainCommand(newModuleGraph(newConfigRef()), newIdentCache())
diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim
index 2c59a9097..460d0b4a5 100644
--- a/compiler/modulegraphs.nim
+++ b/compiler/modulegraphs.nim
@@ -25,7 +25,7 @@
 ## - Its dependent module stays the same.
 ##
 
-import ast, intsets, tables, options, rod
+import ast, intsets, tables, options, rod, msgs, hashes, idents
 
 type
   ModuleGraph* = ref object
@@ -34,70 +34,88 @@ type
     deps*: IntSet # the dependency graph or potentially its transitive closure.
     suggestMode*: bool # whether we are in nimsuggest mode or not.
     invalidTransitiveClosure: bool
-    inclToMod*: Table[int32, int32] # mapping of include file to the
-                                    # first module that included it
-    importStack*: seq[int32]  # The current import stack. Used for detecting recursive
-                              # module dependencies.
+    inclToMod*: Table[FileIndex, FileIndex] # mapping of include file to the
+                                            # first module that included it
+    importStack*: seq[FileIndex]  # The current import stack. Used for detecting recursive
+                                  # module dependencies.
     backend*: RootRef # minor hack so that a backend can extend this easily
     config*: ConfigRef
     doStopCompile*: proc(): bool {.closure.}
     usageSym*: PSym # for nimsuggest
     owners*: seq[PSym]
     methods*: seq[tuple[methods: TSymSeq, dispatcher: PSym]]
+    systemModule*: PSym
+    sysTypes*: array[TTypeKind, PType]
+    compilerprocs*: TStrTable
+    exposed*: TStrTable
+    intTypeCache*: array[-5..64, PType]
+    opContains*, opNot*: PSym
+
+proc hash*(x: FileIndex): Hash {.borrow.}
 
 {.this: g.}
 
 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.magic = m
+
 proc newModuleGraph*(config: ConfigRef = nil): ModuleGraph =
   result = ModuleGraph()
   initStrTable(result.packageSyms)
   result.deps = initIntSet()
   result.modules = @[]
   result.importStack = @[]
-  result.inclToMod = initTable[int32, int32]()
+  result.inclToMod = initTable[FileIndex, FileIndex]()
   if config.isNil:
     result.config = newConfigRef()
   else:
     result.config = config
   result.owners = @[]
   result.methods = @[]
+  initStrTable(result.compilerprocs)
+  initStrTable(result.exposed)
+  result.opNot = createMagic(result, "not", mNot)
+  result.opContains = createMagic(result, "contains", mInSet)
 
 proc resetAllModules*(g: ModuleGraph) =
   initStrTable(packageSyms)
   deps = initIntSet()
   modules = @[]
   importStack = @[]
-  inclToMod = initTable[int32, int32]()
+  inclToMod = initTable[FileIndex, FileIndex]()
   usageSym = nil
   owners = @[]
   methods = @[]
+  initStrTable(compilerprocs)
+  initStrTable(exposed)
 
-proc getModule*(g: ModuleGraph; fileIdx: int32): PSym =
-  if fileIdx >= 0 and fileIdx < modules.len:
-    result = modules[fileIdx]
+proc getModule*(g: ModuleGraph; fileIdx: FileIndex): PSym =
+  if fileIdx.int32 >= 0 and fileIdx.int32 < modules.len:
+    result = modules[fileIdx.int32]
 
 proc dependsOn(a, b: int): int {.inline.} = (a shl 15) + b
 
-proc addDep*(g: ModuleGraph; m: PSym, dep: int32) =
-  assert m.position == m.info.fileIndex
+proc addDep*(g: ModuleGraph; m: PSym, dep: FileIndex) =
+  assert m.position == m.info.fileIndex.int32
   addModuleDep(m.info.fileIndex, dep, isIncludeFile = false)
   if suggestMode:
-    deps.incl m.position.dependsOn(dep)
+    deps.incl m.position.dependsOn(dep.int)
     # we compute the transitive closure later when quering the graph lazily.
     # this improve efficiency quite a lot:
     #invalidTransitiveClosure = true
 
-proc addIncludeDep*(g: ModuleGraph; module, includeFile: int32) =
+proc addIncludeDep*(g: ModuleGraph; module, includeFile: FileIndex) =
   addModuleDep(module, includeFile, isIncludeFile = true)
   discard hasKeyOrPut(inclToMod, includeFile, module)
 
-proc parentModule*(g: ModuleGraph; fileIdx: int32): int32 =
+proc parentModule*(g: ModuleGraph; fileIdx: FileIndex): FileIndex =
   ## returns 'fileIdx' if the file belonging to this index is
   ## directly used as a module or else the module that first
   ## references this include file.
-  if fileIdx >= 0 and fileIdx < modules.len and modules[fileIdx] != nil:
+  if fileIdx.int32 >= 0 and fileIdx.int32 < modules.len and modules[fileIdx.int32] != nil:
     result = fileIdx
   else:
     result = inclToMod.getOrDefault(fileIdx)
@@ -111,11 +129,11 @@ proc transitiveClosure(g: var IntSet; n: int) =
           if g.contains(i.dependsOn(k)) and g.contains(k.dependsOn(j)):
             g.incl i.dependsOn(j)
 
-proc markDirty*(g: ModuleGraph; fileIdx: int32) =
+proc markDirty*(g: ModuleGraph; fileIdx: FileIndex) =
   let m = getModule fileIdx
   if m != nil: incl m.flags, sfDirty
 
-proc markClientsDirty*(g: ModuleGraph; fileIdx: int32) =
+proc markClientsDirty*(g: ModuleGraph; fileIdx: FileIndex) =
   # we need to mark its dependent modules D as dirty right away because after
   # nimsuggest is done with this module, the module's dirty flag will be
   # cleared but D still needs to be remembered as 'dirty'.
@@ -126,7 +144,7 @@ proc markClientsDirty*(g: ModuleGraph; fileIdx: int32) =
   # every module that *depends* on this file is also dirty:
   for i in 0i32..<modules.len.int32:
     let m = modules[i]
-    if m != nil and deps.contains(i.dependsOn(fileIdx)):
+    if m != nil and deps.contains(i.dependsOn(fileIdx.int)):
       incl m.flags, sfDirty
 
 proc isDirty*(g: ModuleGraph; m: PSym): bool =
diff --git a/compiler/modulepaths.nim b/compiler/modulepaths.nim
index 878c22cf8..daa55c2ba 100644
--- a/compiler/modulepaths.nim
+++ b/compiler/modulepaths.nim
@@ -113,16 +113,16 @@ when false:
       localError(pkg.info, "package name must be an identifier or string literal")
       result = ""
 
-proc getModuleName*(n: PNode): string =
+proc getModuleName*(conf: ConfigRef; n: PNode): string =
   # This returns a short relative module name without the nim extension
   # e.g. like "system", "importer" or "somepath/module"
   # The proc won't perform any checks that the path is actually valid
   case n.kind
   of nkStrLit, nkRStrLit, nkTripleStrLit:
     try:
-      result = pathSubs(n.strVal, n.info.toFullPath().splitFile().dir)
+      result = pathSubs(conf, n.strVal, n.info.toFullPath().splitFile().dir)
     except ValueError:
-      localError(n.info, "invalid path: " & n.strVal)
+      localError(conf, n.info, "invalid path: " & n.strVal)
       result = n.strVal
   of nkIdent:
     result = n.ident.s
@@ -137,7 +137,7 @@ proc getModuleName*(n: PNode): string =
       n.sons[0] = n.sons[1]
       n.sons[1] = n.sons[2]
       n.sons.setLen(2)
-      return getModuleName(n.sons[0])
+      return getModuleName(conf, n.sons[0])
     when false:
       if n1.kind == nkPrefix and n1[0].kind == nkIdent and n1[0].ident.s == "$":
         if n0.kind == nkIdent and n0.ident.s == "/":
@@ -146,16 +146,16 @@ proc getModuleName*(n: PNode): string =
           localError(n.info, "only '/' supported with $package notation")
           result = ""
     else:
-      let modname = getModuleName(n[2])
+      let modname = getModuleName(conf, n[2])
       if $n1 == "std":
         template attempt(a) =
           let x = addFileExt(a, "nim")
           if fileExists(x): return x
         for candidate in stdlibDirs:
-          attempt(options.libpath / candidate / modname)
+          attempt(conf.libpath / candidate / modname)
 
       # hacky way to implement 'x / y /../ z':
-      result = getModuleName(n1)
+      result = getModuleName(conf, n1)
       result.add renderTree(n0, {renderNoComments})
       result.add modname
   of nkPrefix:
@@ -169,19 +169,19 @@ proc getModuleName*(n: PNode): string =
   of nkDotExpr:
     result = renderTree(n, {renderNoComments}).replace(".", "/")
   of nkImportAs:
-    result = getModuleName(n.sons[0])
+    result = getModuleName(conf, n.sons[0])
   else:
-    localError(n.info, errGenerated, "invalid module name: '$1'" % n.renderTree)
+    localError(conf, n.info, "invalid module name: '$1'" % n.renderTree)
     result = ""
 
-proc checkModuleName*(n: PNode; doLocalError=true): int32 =
+proc checkModuleName*(conf: ConfigRef; n: PNode; doLocalError=true): FileIndex =
   # This returns the full canonical path for a given module import
-  let modulename = n.getModuleName
-  let fullPath = findModule(modulename, n.info.toFullPath)
+  let modulename = getModuleName(conf, n)
+  let fullPath = findModule(conf, modulename, n.info.toFullPath)
   if fullPath.len == 0:
     if doLocalError:
       let m = if modulename.len > 0: modulename else: $n
-      localError(n.info, errCannotOpenFile, m)
+      localError(conf, n.info, "cannot open file: " & m)
     result = InvalidFileIDX
   else:
-    result = fullPath.fileInfoIdx
+    result = fileInfoIdx(conf, fullPath)
diff --git a/compiler/modules.nim b/compiler/modules.nim
index a3be5a518..5d1eba1f2 100644
--- a/compiler/modules.nim
+++ b/compiler/modules.nim
@@ -11,117 +11,13 @@
 
 import
   ast, astalgo, magicsys, std / sha1, rodread, msgs, cgendata, sigmatch, options,
-  idents, os, lexer, idgen, passes, syntaxes, llstream, modulegraphs, rod
-
-when false:
-  type
-    TNeedRecompile* = enum Maybe, No, Yes, Probing, Recompiled
-    THashStatus* = enum hashNotTaken, hashCached, hashHasChanged, hashNotChanged
-
-    TModuleInMemory* = object
-      hash*: SecureHash
-      deps*: seq[int32] ## XXX: slurped files are currently not tracked
-
-      needsRecompile*: TNeedRecompile
-      hashStatus*: THashStatus
-
-  var
-    gCompiledModules: seq[PSym] = @[]
-    gMemCacheData*: seq[TModuleInMemory] = @[]
-      ## XXX: we should implement recycling of file IDs
-      ## if the user keeps renaming modules, the file IDs will keep growing
-    gFuzzyGraphChecking*: bool # nimsuggest uses this. XXX figure out why.
-
-  proc hashChanged(fileIdx: int32): bool =
-    internalAssert fileIdx >= 0 and fileIdx < gMemCacheData.len
-
-    template updateStatus =
-      gMemCacheData[fileIdx].hashStatus = if result: hashHasChanged
-                                         else: hashNotChanged
-      # echo "TESTING Hash: ", fileIdx.toFilename, " ", result
-
-    case gMemCacheData[fileIdx].hashStatus
-    of hashHasChanged:
-      result = true
-    of hashNotChanged:
-      result = false
-    of hashCached:
-      let newHash = secureHashFile(fileIdx.toFullPath)
-      result = newHash != gMemCacheData[fileIdx].hash
-      gMemCacheData[fileIdx].hash = newHash
-      updateStatus()
-    of hashNotTaken:
-      gMemCacheData[fileIdx].hash = secureHashFile(fileIdx.toFullPath)
-      result = true
-      updateStatus()
-
-  proc doHash(fileIdx: int32) =
-    if gMemCacheData[fileIdx].hashStatus == hashNotTaken:
-      # echo "FIRST Hash: ", fileIdx.ToFilename
-      gMemCacheData[fileIdx].hash = secureHashFile(fileIdx.toFullPath)
-
-  proc resetModule*(fileIdx: int32) =
-    # echo "HARD RESETTING ", fileIdx.toFilename
-    if fileIdx <% gMemCacheData.len:
-      gMemCacheData[fileIdx].needsRecompile = Yes
-    if fileIdx <% gCompiledModules.len:
-      gCompiledModules[fileIdx] = nil
-    if fileIdx <% cgendata.gModules.len:
-      cgendata.gModules[fileIdx] = nil
-
-  proc resetModule*(module: PSym) =
-    let conflict = getModule(module.position.int32)
-    if conflict == nil: return
-    doAssert conflict == module
-    resetModule(module.position.int32)
-    initStrTable(module.tab)
-
-  proc resetAllModules* =
-    for i in 0..gCompiledModules.high:
-      if gCompiledModules[i] != nil:
-        resetModule(i.int32)
-    resetPackageCache()
-    # for m in cgenModules(): echo "CGEN MODULE FOUND"
-
-  proc resetAllModulesHard* =
-    resetPackageCache()
-    gCompiledModules.setLen 0
-    gMemCacheData.setLen 0
-    magicsys.resetSysTypes()
-    # XXX
-    #gOwners = @[]
-
-  proc checkDepMem(fileIdx: int32): TNeedRecompile =
-    template markDirty =
-      resetModule(fileIdx)
-      return Yes
-
-    if gFuzzyGraphChecking:
-      if gMemCacheData[fileIdx].needsRecompile != Maybe:
-        return gMemCacheData[fileIdx].needsRecompile
-    else:
-      # cycle detection: We claim that a cycle does no harm.
-      if gMemCacheData[fileIdx].needsRecompile == Probing:
-        return No
-
-    if optForceFullMake in gGlobalOptions or hashChanged(fileIdx):
-      markDirty()
-
-    if gMemCacheData[fileIdx].deps != nil:
-      gMemCacheData[fileIdx].needsRecompile = Probing
-      for dep in gMemCacheData[fileIdx].deps:
-        let d = checkDepMem(dep)
-        if d in {Yes, Recompiled}:
-          # echo fileIdx.toFilename, " depends on ", dep.toFilename, " ", d
-          markDirty()
-
-    gMemCacheData[fileIdx].needsRecompile = No
-    return No
-
-proc resetSystemArtifacts*() =
-  magicsys.resetSysTypes()
-
-proc newModule(graph: ModuleGraph; fileIdx: int32): PSym =
+  idents, os, lexer, idgen, passes, syntaxes, llstream, modulegraphs, rod,
+  configuration
+
+proc resetSystemArtifacts*(g: ModuleGraph) =
+  magicsys.resetSysTypes(g)
+
+proc newModule(graph: ModuleGraph; fileIdx: FileIndex): PSym =
   # We cannot call ``newSym`` here, because we have to circumvent the ID
   # mechanism, which we do in order to assign each module a persistent ID.
   new(result)
@@ -130,11 +26,11 @@ proc newModule(graph: ModuleGraph; fileIdx: int32): PSym =
   let filename = fileIdx.toFullPath
   result.name = getIdent(splitFile(filename).name)
   if not isNimIdentifier(result.name.s):
-    rawMessage(errInvalidModuleName, result.name.s)
+    rawMessage(graph.config, errGenerated, "invalid module name: " & result.name.s)
 
   result.info = newLineInfo(fileIdx, 1, 1)
   let
-    pck = getPackageName(filename)
+    pck = getPackageName(graph.config, filename)
     pck2 = if pck.len > 0: pck else: "unknown"
     pack = getIdent(pck2)
   var packSym = graph.packageSyms.strTableGet(pack)
@@ -144,9 +40,9 @@ proc newModule(graph: ModuleGraph; fileIdx: int32): PSym =
     graph.packageSyms.strTableAdd(packSym)
 
   result.owner = packSym
-  result.position = fileIdx
+  result.position = int fileIdx
 
-  growCache graph.modules, fileIdx
+  growCache graph.modules, int fileIdx
   graph.modules[result.position] = result
 
   incl(result.flags, sfUsed)
@@ -154,11 +50,11 @@ proc newModule(graph: ModuleGraph; fileIdx: int32): 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(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 " & existing.info.fileIndex.toFullPath)
   # strTableIncl() for error corrections:
   discard strTableIncl(packSym.tab, result)
 
-proc compileModule*(graph: ModuleGraph; fileIdx: int32; cache: IdentCache, flags: TSymFlags): PSym =
+proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; cache: IdentCache, flags: TSymFlags): PSym =
   result = graph.getModule(fileIdx)
   if result == nil:
     #growCache gMemCacheData, fileIdx
@@ -170,7 +66,7 @@ proc compileModule*(graph: ModuleGraph; fileIdx: int32; cache: IdentCache, flags
       gMainPackageId = result.owner.id
 
     when false:
-      if gCmd in {cmdCompileToC, cmdCompileToCpp, cmdCheck, cmdIdeTools}:
+      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")
@@ -179,7 +75,7 @@ proc compileModule*(graph: ModuleGraph; fileIdx: int32; cache: IdentCache, flags
         discard
     result.id = getModuleId(fileIdx, toFullPath(fileIdx))
     discard processModule(graph, result,
-      if sfMainModule in flags and gProjectIsStdin: stdin.llStreamOpen else: nil,
+      if sfMainModule in flags and graph.config.projectIsStdin: stdin.llStreamOpen else: nil,
       rd, cache)
     #if optCaasEnabled in gGlobalOptions:
     #  gMemCacheData[fileIdx].needsRecompile = Recompiled
@@ -190,7 +86,7 @@ proc compileModule*(graph: ModuleGraph; fileIdx: int32; cache: IdentCache, flags
     initStrTable(result.tab)
     result.ast = nil
     discard processModule(graph, result,
-      if sfMainModule in flags and gProjectIsStdin: stdin.llStreamOpen else: nil,
+      if sfMainModule in flags and graph.config.projectIsStdin: stdin.llStreamOpen else: nil,
       nil, cache)
     graph.markClientsDirty(fileIdx)
     when false:
@@ -199,41 +95,44 @@ proc compileModule*(graph: ModuleGraph; fileIdx: int32; cache: IdentCache, flags
       else:
         result = gCompiledModules[fileIdx]
 
-proc importModule*(graph: ModuleGraph; s: PSym, fileIdx: int32;
+proc importModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex;
                    cache: IdentCache): PSym {.procvar.} =
   # this is called by the semantic checking phase
+  assert graph.config != nil
   result = compileModule(graph, fileIdx, cache, {})
   graph.addDep(s, fileIdx)
   #if sfSystemModule in result.flags:
   #  localError(result.info, errAttemptToRedefine, result.name.s)
   # restore the notes for outer module:
-  gNotes = if s.owner.id == gMainPackageId: gMainPackageNotes
-           else: ForeignPackageNotes
+  graph.config.notes =
+      if s.owner.id == gMainPackageId: graph.config.mainPackageNotes
+      else: graph.config.foreignPackageNotes
 
-proc includeModule*(graph: ModuleGraph; s: PSym, fileIdx: int32;
+proc includeModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex;
                     cache: IdentCache): PNode {.procvar.} =
-  result = syntaxes.parseFile(fileIdx, cache)
+  result = syntaxes.parseFile(fileIdx, cache, graph.config)
   graph.addDep(s, fileIdx)
-  graph.addIncludeDep(s.position.int32, fileIdx)
+  graph.addIncludeDep(s.position.FileIndex, fileIdx)
 
 proc compileSystemModule*(graph: ModuleGraph; cache: IdentCache) =
-  if magicsys.systemModule == nil:
-    systemFileIdx = fileInfoIdx(options.libpath/"system.nim")
+  if graph.systemModule == nil:
+    systemFileIdx = fileInfoIdx(graph.config, graph.config.libpath / "system.nim")
     discard graph.compileModule(systemFileIdx, cache, {sfSystemModule})
 
-proc wantMainModule* =
-  if gProjectFull.len == 0:
-    fatal(gCmdLineInfo, errCommandExpectsFilename)
-  gProjectMainIdx = addFileExt(gProjectFull, NimExt).fileInfoIdx
+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
 
 proc compileProject*(graph: ModuleGraph; cache: IdentCache;
-                     projectFileIdx = -1'i32) =
-  wantMainModule()
-  let systemFileIdx = fileInfoIdx(options.libpath / "system.nim")
-  let projectFile = if projectFileIdx < 0: gProjectMainIdx else: projectFileIdx
+                     projectFileIdx = InvalidFileIDX) =
+  let conf = graph.config
+  wantMainModule(conf)
+  let systemFileIdx = fileInfoIdx(conf, conf.libpath / "system.nim")
+  let projectFile = if projectFileIdx == InvalidFileIDX: FileIndex(conf.projectMainIdx) else: projectFileIdx
   graph.importStack.add projectFile
   if projectFile == systemFileIdx:
     discard graph.compileModule(projectFile, cache, {sfMainModule, sfSystemModule})
@@ -242,7 +141,7 @@ proc compileProject*(graph: ModuleGraph; cache: IdentCache;
     discard graph.compileModule(projectFile, cache, {sfMainModule})
 
 proc makeModule*(graph: ModuleGraph; filename: string): PSym =
-  result = graph.newModule(fileInfoIdx filename)
+  result = graph.newModule(fileInfoIdx(graph.config, filename))
   result.id = getID()
 
 proc makeStdinModule*(graph: ModuleGraph): PSym = graph.makeModule"stdin"
diff --git a/compiler/msgs.nim b/compiler/msgs.nim
index 70504cfc9..533d3a57f 100644
--- a/compiler/msgs.nim
+++ b/compiler/msgs.nim
@@ -8,476 +8,13 @@
 #
 
 import
-  options, strutils, os, tables, ropes, platform, terminal, macros
+  options, strutils, os, tables, ropes, platform, terminal, macros,
+  configuration
 
-type
-  TMsgKind* = enum
-    errUnknown, errInternal, errIllFormedAstX, errCannotOpenFile, errGenerated,
-    errXCompilerDoesNotSupportCpp, errStringLiteralExpected,
-    errIntLiteralExpected, errInvalidCharacterConstant,
-    errClosingTripleQuoteExpected, errClosingQuoteExpected,
-    errTabulatorsAreNotAllowed, errInvalidToken, errLineTooLong,
-    errInvalidNumber, errInvalidNumberOctalCode, errNumberOutOfRange,
-    errNnotAllowedInCharacter, errClosingBracketExpected, errMissingFinalQuote,
-    errIdentifierExpected, errNewlineExpected, errInvalidModuleName,
-    errOperatorExpected, errTokenExpected, errStringAfterIncludeExpected,
-    errRecursiveDependencyX, errOnOrOffExpected, errNoneSpeedOrSizeExpected,
-    errInvalidPragma, errUnknownPragma, errInvalidDirectiveX,
-    errAtPopWithoutPush, errEmptyAsm, errInvalidIndentation,
-    errExceptionExpected, errExceptionAlreadyHandled,
-    errYieldNotAllowedHere, errYieldNotAllowedInTryStmt,
-    errInvalidNumberOfYieldExpr, errCannotReturnExpr,
-    errNoReturnWithReturnTypeNotAllowed, errAttemptToRedefine,
-    errStmtInvalidAfterReturn, errStmtExpected, errInvalidLabel,
-    errInvalidCmdLineOption, errCmdLineArgExpected, errCmdLineNoArgExpected,
-    errInvalidVarSubstitution, errUnknownVar, errUnknownCcompiler,
-    errOnOrOffExpectedButXFound, errOnOffOrListExpectedButXFound,
-    errNoneBoehmRefcExpectedButXFound,
-    errNoneSpeedOrSizeExpectedButXFound, errGuiConsoleOrLibExpectedButXFound,
-    errUnknownOS, errUnknownCPU, errGenOutExpectedButXFound,
-    errArgsNeedRunOption, errInvalidMultipleAsgn, errColonOrEqualsExpected,
-    errExprExpected, errUndeclaredField,
-    errUndeclaredRoutine, errUseQualifier,
-    errTypeExpected,
-    errSystemNeeds, errExecutionOfProgramFailed, errNotOverloadable,
-    errInvalidArgForX, errStmtHasNoEffect, errXExpectsTypeOrValue,
-    errXExpectsArrayType, errIteratorCannotBeInstantiated, errExprXAmbiguous,
-    errConstantDivisionByZero, errOrdinalTypeExpected,
-    errOrdinalOrFloatTypeExpected, errOverOrUnderflow,
-    errCannotEvalXBecauseIncompletelyDefined, errChrExpectsRange0_255,
-    errDynlibRequiresExportc, errUndeclaredFieldX, errNilAccess,
-    errIndexOutOfBounds, errIndexTypesDoNotMatch, errBracketsInvalidForType,
-    errValueOutOfSetBounds, errFieldInitTwice, errFieldNotInit,
-    errExprXCannotBeCalled, errExprHasNoType, errExprXHasNoType,
-    errCastNotInSafeMode, errExprCannotBeCastToX, errCommaOrParRiExpected,
-    errCurlyLeOrParLeExpected, errSectionExpected, errRangeExpected,
-    errMagicOnlyInSystem, errPowerOfTwoExpected,
-    errStringMayNotBeEmpty, errCallConvExpected, errProcOnlyOneCallConv,
-    errSymbolMustBeImported, errExprMustBeBool, errConstExprExpected,
-    errDuplicateCaseLabel, errRangeIsEmpty, errSelectorMustBeOfCertainTypes,
-    errSelectorMustBeOrdinal, errOrdXMustNotBeNegative, errLenXinvalid,
-    errWrongNumberOfVariables, errExprCannotBeRaised, errBreakOnlyInLoop,
-    errTypeXhasUnknownSize, errConstNeedsConstExpr, errConstNeedsValue,
-    errResultCannotBeOpenArray, errSizeTooBig, errSetTooBig,
-    errBaseTypeMustBeOrdinal, errInheritanceOnlyWithNonFinalObjects,
-    errInheritanceOnlyWithEnums, errIllegalRecursionInTypeX,
-    errCannotInstantiateX, errExprHasNoAddress, errXStackEscape,
-    errVarForOutParamNeededX,
-    errPureTypeMismatch, errTypeMismatch, errButExpected, errButExpectedX,
-    errAmbiguousCallXYZ, errWrongNumberOfArguments,
-    errWrongNumberOfArgumentsInCall,
-    errMissingGenericParamsForTemplate,
-    errXCannotBePassedToProcVar,
-    errXCannotBeInParamDecl, errPragmaOnlyInHeaderOfProcX, errImplOfXNotAllowed,
-    errImplOfXexpected, errNoSymbolToBorrowFromFound, errDiscardValueX,
-    errInvalidDiscard, errIllegalConvFromXtoY, errCannotBindXTwice,
-    errInvalidOrderInArrayConstructor,
-    errInvalidOrderInEnumX, errEnumXHasHoles, errExceptExpected, errInvalidTry,
-    errOptionExpected, errXisNoLabel, errNotAllCasesCovered,
-    errUnknownSubstitionVar, errComplexStmtRequiresInd, errXisNotCallable,
-    errNoPragmasAllowedForX, errNoGenericParamsAllowedForX,
-    errInvalidParamKindX, errDefaultArgumentInvalid, errNamedParamHasToBeIdent,
-    errNoReturnTypeForX, errConvNeedsOneArg, errInvalidPragmaX,
-    errXNotAllowedHere, errInvalidControlFlowX,
-    errXisNoType, errCircumNeedsPointer, errInvalidExpression,
-    errInvalidExpressionX, errEnumHasNoValueX, errNamedExprExpected,
-    errNamedExprNotAllowed, errXExpectsOneTypeParam,
-    errArrayExpectsTwoTypeParams, errInvalidVisibilityX, errInitHereNotAllowed,
-    errXCannotBeAssignedTo, errIteratorNotAllowed, errXNeedsReturnType,
-    errNoReturnTypeDeclared,
-    errNoCommand, errInvalidCommandX, errXOnlyAtModuleScope,
-    errXNeedsParamObjectType,
-    errTemplateInstantiationTooNested, errInstantiationFrom,
-    errInvalidIndexValueForTuple, errCommandExpectsFilename,
-    errMainModuleMustBeSpecified,
-    errXExpected,
-    errTIsNotAConcreteType,
-    errCastToANonConcreteType,
-    errInvalidSectionStart, errGridTableNotImplemented, errGeneralParseError,
-    errNewSectionExpected, errWhitespaceExpected, errXisNoValidIndexFile,
-    errCannotRenderX, errVarVarTypeNotAllowed, errInstantiateXExplicitly,
-    errOnlyACallOpCanBeDelegator, errUsingNoSymbol,
-    errMacroBodyDependsOnGenericTypes,
-    errDestructorNotGenericEnough,
-    errInlineIteratorsAsProcParams,
-    errXExpectsTwoArguments,
-    errXExpectsObjectTypes, errXcanNeverBeOfThisSubtype, errTooManyIterations,
-    errCannotInterpretNodeX, errFieldXNotFound, errInvalidConversionFromTypeX,
-    errAssertionFailed, errCannotGenerateCodeForX, errXRequiresOneArgument,
-    errUnhandledExceptionX, errCyclicTree, errXisNoMacroOrTemplate,
-    errXhasSideEffects, errIteratorExpected, errLetNeedsInit,
-    errThreadvarCannotInit, errWrongSymbolX, errIllegalCaptureX,
-    errXCannotBeClosure, errXMustBeCompileTime,
-    errCannotInferTypeOfTheLiteral,
-    errCannotInferReturnType,
-    errCannotInferStaticParam,
-    errGenericLambdaNotAllowed,
-    errProcHasNoConcreteType,
-    errCompilerDoesntSupportTarget,
-    errInOutFlagNotExtern,
-    errUser,
-    warnCannotOpenFile,
-    warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit,
-    warnDeprecated, warnConfigDeprecated,
-    warnSmallLshouldNotBeUsed, warnUnknownMagic, warnRedefinitionOfLabel,
-    warnUnknownSubstitutionX, warnLanguageXNotSupported,
-    warnFieldXNotSupported, warnCommentXIgnored,
-    warnNilStatement, warnTypelessParam,
-    warnUseBase, warnWriteToForeignHeap, warnUnsafeCode,
-    warnEachIdentIsTuple, warnShadowIdent,
-    warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2,
-    warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnResultShadowed,
-    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\'",
-    errGenerated: "$1",
-    errXCompilerDoesNotSupportCpp: "\'$1\' compiler does not support C++",
-    errStringLiteralExpected: "string literal expected",
-    errIntLiteralExpected: "integer literal expected",
-    errInvalidCharacterConstant: "invalid character constant",
-    errClosingTripleQuoteExpected: "closing \"\"\" expected, but end of file reached",
-    errClosingQuoteExpected: "closing \" expected",
-    errTabulatorsAreNotAllowed: "tabulators are not allowed",
-    errInvalidToken: "invalid token: $1",
-    errLineTooLong: "line too long",
-    errInvalidNumber: "$1 is not a valid number",
-    errInvalidNumberOctalCode: "$1 is not a valid number; did you mean octal? Then use one of '0o', '0c' or '0C'.",
-    errNumberOutOfRange: "number $1 out of valid range",
-    errNnotAllowedInCharacter: "\\n not allowed in character literal",
-    errClosingBracketExpected: "closing ']' expected, but end of file reached",
-    errMissingFinalQuote: "missing final \' for character literal",
-    errIdentifierExpected: "identifier expected, but found \'$1\'",
-    errNewlineExpected: "newline expected, but found \'$1\'",
-    errInvalidModuleName: "invalid module name: '$1'",
-    errOperatorExpected: "operator expected, but found \'$1\'",
-    errTokenExpected: "\'$1\' expected",
-    errStringAfterIncludeExpected: "string after \'include\' expected",
-    errRecursiveDependencyX: "recursive dependency: \'$1\'",
-    errOnOrOffExpected: "\'on\' or \'off\' expected",
-    errNoneSpeedOrSizeExpected: "\'none\', \'speed\' or \'size\' expected",
-    errInvalidPragma: "invalid pragma",
-    errUnknownPragma: "unknown pragma: \'$1\'",
-    errInvalidDirectiveX: "invalid directive: \'$1\'",
-    errAtPopWithoutPush: "\'pop\' without a \'push\' pragma",
-    errEmptyAsm: "empty asm statement",
-    errInvalidIndentation: "invalid indentation",
-    errExceptionExpected: "exception expected",
-    errExceptionAlreadyHandled: "exception already handled",
-    errYieldNotAllowedHere: "'yield' only allowed in an iterator",
-    errYieldNotAllowedInTryStmt: "'yield' cannot be used within 'try' in a non-inlined iterator",
-    errInvalidNumberOfYieldExpr: "invalid number of \'yield\' expressions",
-    errCannotReturnExpr: "current routine cannot return an expression",
-    errNoReturnWithReturnTypeNotAllowed: "routines with NoReturn pragma are not allowed to have return type",
-    errAttemptToRedefine: "redefinition of \'$1\'",
-    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",
-    errNoneBoehmRefcExpectedButXFound: "'none', 'boehm' or 'refc' expected, but '$1' found",
-    errNoneSpeedOrSizeExpectedButXFound: "'none', 'speed' or 'size' expected, but '$1' found",
-    errGuiConsoleOrLibExpectedButXFound: "'gui', 'console' or 'lib' expected, but '$1' found",
-    errUnknownOS: "unknown OS: '$1'",
-    errUnknownCPU: "unknown CPU: '$1'",
-    errGenOutExpectedButXFound: "'c', 'c++' or 'yaml' expected, but '$1' found",
-    errArgsNeedRunOption: "arguments can only be given if the '--run' option is selected",
-    errInvalidMultipleAsgn: "multiple assignment is not allowed",
-    errColonOrEqualsExpected: "\':\' or \'=\' expected, but found \'$1\'",
-    errExprExpected: "expression 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: "overloaded \'$1\' leads to ambiguous calls",
-    errInvalidArgForX: "invalid argument for \'$1\'",
-    errStmtHasNoEffect: "statement has no effect",
-    errXExpectsTypeOrValue: "\'$1\' expects a type or value",
-    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: "division by zero",
-    errOrdinalTypeExpected: "ordinal type expected",
-    errOrdinalOrFloatTypeExpected: "ordinal or float type expected",
-    errOverOrUnderflow: "over- or underflow",
-    errCannotEvalXBecauseIncompletelyDefined: "cannot evaluate '$1' because type is not defined completely",
-    errChrExpectsRange0_255: "\'chr\' expects an int in the range 0..255",
-    errDynlibRequiresExportc: "\'dynlib\' requires \'exportc\'",
-    errUndeclaredFieldX: "undeclared field: \'$1\'",
-    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",
-    errFieldInitTwice: "field initialized twice: \'$1\'",
-    errFieldNotInit: "field \'$1\' not initialized",
-    errExprXCannotBeCalled: "expression \'$1\' cannot be called",
-    errExprHasNoType: "expression has no type",
-    errExprXHasNoType: "expression \'$1\' has no type (or is ambiguous)",
-    errCastNotInSafeMode: "\'cast\' not allowed in safe mode",
-    errExprCannotBeCastToX: "expression cannot be cast to $1",
-    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",
-    errSelectorMustBeOfCertainTypes: "selector must be of an ordinal type, float or string",
-    errSelectorMustBeOrdinal: "selector must be of an ordinal type",
-    errOrdXMustNotBeNegative: "ord($1) must not be negative",
-    errLenXinvalid: "len($1) must be less than 32768",
-    errWrongNumberOfVariables: "wrong number of variables",
-    errExprCannotBeRaised: "only a 'ref object' can be raised",
-    errBreakOnlyInLoop: "'break' only allowed in loop construct",
-    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",
-    errSetTooBig: "set is too large",
-    errBaseTypeMustBeOrdinal: "base type of a set must be an ordinal",
-    errInheritanceOnlyWithNonFinalObjects: "inheritance only works with non-final objects",
-    errInheritanceOnlyWithEnums: "inheritance only works with an enum",
-    errIllegalRecursionInTypeX: "illegal recursion in type \'$1\'",
-    errCannotInstantiateX: "cannot instantiate: \'$1\'",
-    errExprHasNoAddress: "expression has no address",
-    errXStackEscape: "address of '$1' may not escape its stack frame",
-    errVarForOutParamNeededX: "for a \'var\' type a variable needs to be passed; but '$1' is immutable",
-    errPureTypeMismatch: "type mismatch",
-    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: "\'$1\' cannot be passed to a procvar",
-    errXCannotBeInParamDecl: "$1 cannot be declared in parameter declaration",
-    errPragmaOnlyInHeaderOfProcX: "pragmas are only allowed in the header of a proc; redefinition of $1",
-    errImplOfXNotAllowed: "implementation of \'$1\' is not allowed",
-    errImplOfXexpected: "implementation of \'$1\' expected",
-    errNoSymbolToBorrowFromFound: "no symbol to borrow from found",
-    errDiscardValueX: "value of type '$1' has to be discarded",
-    errInvalidDiscard: "statement returns no value that can be discarded",
-    errIllegalConvFromXtoY: "conversion from $1 to $2 is invalid",
-    errCannotBindXTwice: "cannot bind parameter \'$1\' twice",
-    errInvalidOrderInArrayConstructor: "invalid order in array constructor",
-    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: "option expected, but found \'$1\'",
-    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",
-    errNoGenericParamsAllowedForX: "no generic parameters 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: "invalid pragma: $1",
-    errXNotAllowedHere: "$1 not allowed here",
-    errInvalidControlFlowX: "invalid control flow: $1",
-    errXisNoType: "invalid type: \'$1\'",
-    errCircumNeedsPointer: "'[]' needs a pointer or reference type",
-    errInvalidExpression: "invalid expression",
-    errInvalidExpressionX: "invalid expression: \'$1\'",
-    errEnumHasNoValueX: "enum has no value \'$1\'",
-    errNamedExprExpected: "named expression expected",
-    errNamedExprNotAllowed: "named expression not allowed here",
-    errXExpectsOneTypeParam: "\'$1\' expects one type parameter",
-    errArrayExpectsTwoTypeParams: "array expects two type parameters",
-    errInvalidVisibilityX: "invalid visibility: \'$1\'",
-    errInitHereNotAllowed: "initialization not allowed here",
-    errXCannotBeAssignedTo: "\'$1\' cannot be assigned to",
-    errIteratorNotAllowed: "iterators can only be defined at the module\'s top level",
-    errXNeedsReturnType: "$1 needs a return type",
-    errNoReturnTypeDeclared: "no return type declared",
-    errNoCommand: "no command given",
-    errInvalidCommandX: "invalid command: \'$1\'",
-    errXOnlyAtModuleScope: "\'$1\' is only allowed at top level",
-    errXNeedsParamObjectType: "'$1' needs a parameter that has an object type",
-    errTemplateInstantiationTooNested: "template/macro instantiation too nested",
-    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",
-    errTIsNotAConcreteType: "\'$1\' is not a concrete type.",
-    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: "type \'var var\' is not allowed",
-    errInstantiateXExplicitly: "instantiate '$1' explicitly",
-    errOnlyACallOpCanBeDelegator: "only a call operator can be a delegator",
-    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",
-    errCannotInterpretNodeX: "cannot evaluate \'$1\'",
-    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",
-    errIteratorExpected: "iterator within for loop context expected",
-    errLetNeedsInit: "'let' symbol requires an initialization",
-    errThreadvarCannotInit: "a thread var cannot be initialized explicitly; this would only run for the main thread",
-    errWrongSymbolX: "usage of \'$1\' is a user-defined error",
-    errIllegalCaptureX: "illegal capture '$1'",
-    errXCannotBeClosure: "'$1' cannot have 'closure' calling convention",
-    errXMustBeCompileTime: "'$1' can only be used in compile-time context",
-    errCannotInferTypeOfTheLiteral: "cannot infer the type of the $1",
-    errCannotInferReturnType: "cannot infer the return type of the proc",
-    errCannotInferStaticParam: "cannot infer the value of the static param `$1`",
-    errGenericLambdaNotAllowed: "A nested proc can have generic parameters only when " &
-                                "it is used as an operand to another routine and the types " &
-                                "of the generic paramers can be inferred from the expected signature.",
-    errProcHasNoConcreteType: "'$1' doesn't have a concrete type, due to unspecified generic parameters.",
-    errCompilerDoesntSupportTarget: "The current compiler \'$1\' doesn't support the requested compilation target",
-    errInOutFlagNotExtern: "The `$1` modifier can be used only with imported types",
-    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",
-    warnNilStatement: "'nil' statement is deprecated; use an empty 'discard' statement instead",
-    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.",
-    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", "NilStmt",
-    "TypelessParam", "UseBase", "WriteToForeignHeap",
-    "UnsafeCode", "EachIdentIsTuple", "ShadowIdent",
-    "ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "GcUnsafe2", "Uninit",
-    "GcMem", "Destructor", "LockLevel", "ResultShadowed", "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)
+#type
+#  MsgConfig* = ref object of RootObj
 
 type
-  TNoteKind* = range[warnMin..hintMax] # "notes" are warnings or hints
-  TNoteKinds* = set[TNoteKind]
-
   TFileInfo* = object
     fullPath: string           # This is a canonical full filesystem path
     projPath*: string          # This is relative to the project's root
@@ -495,15 +32,18 @@ type
                                # 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*, col*: int16
-    fileIndex*: int32
+    line*: uint16
+    col*: int16
+    fileIndex*: FileIndex
     when defined(nimpretty):
       offsetA*, offsetB*: int
       commentOffsetA*, commentOffsetB*: int
@@ -517,38 +57,16 @@ type
   ERecoverableError* = object of ValueError
   ESuggestDone* = object of Exception
 
-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)}]
+proc `==`*(a, b: FileIndex): bool {.borrow.}
+
 
 const
-  InvalidFileIDX* = int32(-1)
+  InvalidFileIDX* = FileIndex(-1)
 
 var
-  ForeignPackageNotes*: TNoteKinds = {hintProcessing, warnUnknownMagic,
-    hintQuitCalled, hintExecuting}
-  filenameToIndexTbl = initTable[string, int32]()
+  filenameToIndexTbl = initTable[string, FileIndex]()
   fileInfos*: seq[TFileInfo] = @[]
-  systemFileIdx*: int32
+  systemFileIdx*: FileIndex
 
 proc toCChar*(c: char): string =
   case c
@@ -581,25 +99,36 @@ proc newFileInfo(fullPath, projPath: string): TFileInfo =
   result.shortName = fileName.changeFileExt("")
   result.quotedName = fileName.makeCString
   result.quotedFullName = fullPath.makeCString
-  if optEmbedOrigSrc in gGlobalOptions or true:
-    result.lines = @[]
-
-proc fileInfoKnown*(filename: string): bool =
+  result.lines = @[]
+  when defined(nimpretty):
+    if result.fullPath.len > 0:
+      try:
+        result.fullContent = readFile(result.fullPath)
+      except IOError:
+        #rawMessage(errCannotOpenFile, result.fullPath)
+        # XXX fixme
+        result.fullContent = ""
+
+when defined(nimpretty):
+  proc fileSection*(fid: FileIndex; a, b: int): string =
+    substr(fileInfos[fid.int].fullContent, a, b)
+
+proc fileInfoKnown*(conf: ConfigRef; filename: string): bool =
   var
     canon: string
   try:
-    canon = canonicalizePath(filename)
+    canon = canonicalizePath(conf, filename)
   except:
     canon = filename
   result = filenameToIndexTbl.hasKey(canon)
 
-proc fileInfoIdx*(filename: string; isKnownFile: var bool): int32 =
+proc fileInfoIdx*(conf: ConfigRef; filename: string; isKnownFile: var bool): FileIndex =
   var
     canon: string
     pseudoPath = false
 
   try:
-    canon = canonicalizePath(filename)
+    canon = canonicalizePath(conf, filename)
     shallow(canon)
   except:
     canon = filename
@@ -611,46 +140,39 @@ proc fileInfoIdx*(filename: string; isKnownFile: var bool): int32 =
     result = filenameToIndexTbl[canon]
   else:
     isKnownFile = false
-    result = fileInfos.len.int32
+    result = fileInfos.len.FileIndex
     fileInfos.add(newFileInfo(canon, if pseudoPath: filename
-                                     else: canon.shortenDir))
+                                     else: shortenDir(conf, canon)))
     filenameToIndexTbl[canon] = result
 
-proc fileInfoIdx*(filename: string): int32 =
+proc fileInfoIdx*(conf: ConfigRef; filename: string): FileIndex =
   var dummy: bool
-  result = fileInfoIdx(filename, dummy)
+  result = fileInfoIdx(conf, filename, dummy)
 
-proc newLineInfo*(fileInfoIdx: int32, line, col: int): TLineInfo =
+proc newLineInfo*(fileInfoIdx: FileIndex, line, col: int): TLineInfo =
   result.fileIndex = fileInfoIdx
-  result.line = int16(line)
+  result.line = uint16(line)
   result.col = int16(col)
 
-proc newLineInfo*(filename: string, line, col: int): TLineInfo {.inline.} =
-  result = newLineInfo(filename.fileInfoIdx, line, col)
+proc newLineInfo*(conf: ConfigRef; filename: string, line, col: int): TLineInfo {.inline.} =
+  result = newLineInfo(fileInfoIdx(conf, filename), line, col)
 
-fileInfos.add(newFileInfo("", "command line"))
-var gCmdLineInfo* = newLineInfo(int32(0), 1, 1)
+when false:
+  fileInfos.add(newFileInfo("", "command line"))
+  var gCmdLineInfo* = newLineInfo(FileIndex(0), 1, 1)
 
-fileInfos.add(newFileInfo("", "compilation artifact"))
-var gCodegenLineInfo* = newLineInfo(int32(1), 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*(i: TLineInfo): Rope
-
-var
-  gNotes*: TNoteKinds = NotesVerbosity[1] # defaults to verbosity of 1
-  gErrorCounter*: int = 0     # counts the number of errors
-  gHintCounter*: int = 0
-  gWarnCounter*: int = 0
-  gErrorMax*: int = 1         # stop after gErrorMax errors
-  gMainPackageNotes*: TNoteKinds = NotesVerbosity[1]
+proc sourceLine*(conf: ConfigRef; i: TLineInfo): Rope
 
 proc unknownLineInfo*(): TLineInfo =
-  result.line = int16(-1)
+  result.line = uint16(0)
   result.col = int16(-1)
-  result.fileIndex = -1
+  result.fileIndex = InvalidFileIDX
 
 type
   Severity* {.pure.} = enum ## VS Code only supports these three
@@ -712,32 +234,32 @@ proc getInfoContext*(index: int): TLineInfo =
   if i >=% L: result = unknownLineInfo()
   else: result = msgContext[i]
 
-template toFilename*(fileIdx: int32): string =
-  (if fileIdx < 0: "???" else: fileInfos[fileIdx].projPath)
+template toFilename*(fileIdx: FileIndex): string =
+  (if fileIdx.int32 < 0: "???" else: fileInfos[fileIdx.int32].projPath)
 
-proc toFullPath*(fileIdx: int32): string =
-  if fileIdx < 0: result = "???"
-  else: result = fileInfos[fileIdx].fullPath
+proc toFullPath*(fileIdx: FileIndex): string =
+  if fileIdx.int32 < 0: result = "???"
+  else: result = fileInfos[fileIdx.int32].fullPath
 
-proc setDirtyFile*(fileIdx: int32; filename: string) =
-  assert fileIdx >= 0
-  fileInfos[fileIdx].dirtyFile = filename
+proc setDirtyFile*(fileIdx: FileIndex; filename: string) =
+  assert fileIdx.int32 >= 0
+  fileInfos[fileIdx.int32].dirtyFile = filename
 
-proc setHash*(fileIdx: int32; hash: string) =
-  assert fileIdx >= 0
-  shallowCopy(fileInfos[fileIdx].hash, hash)
+proc setHash*(fileIdx: FileIndex; hash: string) =
+  assert fileIdx.int32 >= 0
+  shallowCopy(fileInfos[fileIdx.int32].hash, hash)
 
-proc getHash*(fileIdx: int32): string =
-  assert fileIdx >= 0
-  shallowCopy(result, fileInfos[fileIdx].hash)
+proc getHash*(fileIdx: FileIndex): string =
+  assert fileIdx.int32 >= 0
+  shallowCopy(result, fileInfos[fileIdx.int32].hash)
 
-proc toFullPathConsiderDirty*(fileIdx: int32): string =
-  if fileIdx < 0:
+proc toFullPathConsiderDirty*(fileIdx: FileIndex): string =
+  if fileIdx.int32 < 0:
     result = "???"
-  elif not fileInfos[fileIdx].dirtyFile.isNil:
-    result = fileInfos[fileIdx].dirtyFile
+  elif not fileInfos[fileIdx.int32].dirtyFile.isNil:
+    result = fileInfos[fileIdx.int32].dirtyFile
   else:
-    result = fileInfos[fileIdx].fullPath
+    result = fileInfos[fileIdx.int32].fullPath
 
 template toFilename*(info: TLineInfo): string =
   info.fileIndex.toFilename
@@ -745,16 +267,16 @@ template toFilename*(info: TLineInfo): string =
 template toFullPath*(info: TLineInfo): string =
   info.fileIndex.toFullPath
 
-proc toMsgFilename*(info: TLineInfo): string =
-  if info.fileIndex < 0:
+proc toMsgFilename*(conf: ConfigRef; info: TLineInfo): string =
+  if info.fileIndex.int32 < 0:
     result = "???"
-  elif gListFullPaths:
-    result = fileInfos[info.fileIndex].fullPath
+  elif optListFullPaths in conf.globalOptions:
+    result = fileInfos[info.fileIndex.int32].fullPath
   else:
-    result = fileInfos[info.fileIndex].projPath
+    result = fileInfos[info.fileIndex.int32].projPath
 
 proc toLinenumber*(info: TLineInfo): int {.inline.} =
-  result = info.line
+  result = int info.line
 
 proc toColumn*(info: TLineInfo): int {.inline.} =
   result = info.col
@@ -771,7 +293,7 @@ proc `??`* (info: TLineInfo, filename: string): bool =
   # only for debugging purposes
   result = filename in info.toFilename
 
-const trackPosInvalidFileIdx* = -2 # special marker so that no suggestions
+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
@@ -783,7 +305,7 @@ type
     msgSkipHook    ## skip message hook even if it is present
   MsgFlags* = set[MsgFlag]
 
-proc msgWriteln*(s: string, flags: MsgFlags = {}) =
+proc msgWriteln*(conf: ConfigRef; s: string, flags: MsgFlags = {}) =
   ## Writes given message string to stderr by default.
   ## If ``--stdout`` option is given, writes to stdout instead. If message hook
   ## is present, then it is used to output message rather than stderr/stdout.
@@ -791,11 +313,11 @@ proc msgWriteln*(s: string, flags: MsgFlags = {}) =
 
   ## This is used for 'nim dump' etc. where we don't have nimsuggest
   ## support.
-  #if gCmd == cmdIdeTools and optCDebug notin gGlobalOptions: return
+  #if conf.cmd == cmdIdeTools and optCDebug notin gGlobalOptions: return
 
   if not isNil(writelnHook) and msgSkipHook notin flags:
     writelnHook(s)
-  elif optStdout in gGlobalOptions or msgStdout in flags:
+  elif optStdout in conf.globalOptions or msgStdout in flags:
     if eStdOut in errorOutputs:
       writeLine(stdout, s)
       flushFile(stdout)
@@ -836,13 +358,13 @@ template callWritelnHook(args: varargs[string, `$`]) =
 template styledMsgWriteln*(args: varargs[typed]) =
   if not isNil(writelnHook):
     callIgnoringStyle(callWritelnHook, nil, args)
-  elif optStdout in gGlobalOptions:
+  elif optStdout in conf.globalOptions:
     if eStdOut in errorOutputs:
       callIgnoringStyle(writeLine, stdout, args)
       flushFile(stdout)
   else:
     if eStdErr in errorOutputs:
-      if optUseColors in gGlobalOptions:
+      if optUseColors in conf.globalOptions:
         callStyledWriteLineStderr(args)
       else:
         callIgnoringStyle(writeLine, stderr, args)
@@ -870,27 +392,27 @@ proc log*(s: string) {.procvar.} =
     f.writeLine(s)
     close(f)
 
-proc quit(msg: TMsgKind) =
-  if defined(debug) or msg == errInternal or hintStackTrace in gNotes:
+proc quit(conf: ConfigRef; msg: TMsgKind) =
+  if defined(debug) or msg == errInternal or hintStackTrace in conf.notes:
     if stackTraceAvailable() and isNil(writelnHook):
       writeStackTrace()
     else:
       styledMsgWriteln(fgRed, "No stack traceback available\n" &
           "To create a stacktrace, rerun compilation with ./koch temp " &
-          options.command & " <file>")
+          conf.command & " <file>")
   quit 1
 
-proc handleError(msg: TMsgKind, eh: TErrorHandling, s: string) =
+proc handleError(conf: ConfigRef; msg: TMsgKind, eh: TErrorHandling, s: string) =
   if msg >= fatalMin and msg <= fatalMax:
-    if gCmd == cmdIdeTools: log(s)
-    quit(msg)
+    if conf.cmd == cmdIdeTools: log(s)
+    quit(conf, msg)
   if msg >= errMin and msg <= errMax:
-    inc(gErrorCounter)
-    options.gExitcode = 1'i8
-    if gErrorCounter >= gErrorMax:
-      quit(msg)
-    elif eh == doAbort and gCmd != cmdIdeTools:
-      quit(msg)
+    inc(conf.errorCounter)
+    conf.exitcode = 1'i8
+    if conf.errorCounter >= conf.errorMax:
+      quit(conf, msg)
+    elif eh == doAbort and conf.cmd != cmdIdeTools:
+      quit(conf, msg)
     elif eh == doRaise:
       raiseRecoverableError(s)
 
@@ -900,26 +422,27 @@ proc `==`*(a, b: TLineInfo): bool =
 proc exactEquals*(a, b: TLineInfo): bool =
   result = a.fileIndex == b.fileIndex and a.line == b.line and a.col == b.col
 
-proc writeContext(lastinfo: TLineInfo) =
+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], getMessageStr(errInstantiationFrom, ""),
+        structuredErrorHook(msgContext[i], instantiationFrom,
                             Severity.Error)
       else:
         styledMsgWriteln(styleBright,
-                         PosFormat % [toMsgFilename(msgContext[i]),
-                                      coordToStr(msgContext[i].line),
+                         PosFormat % [toMsgFilename(conf, msgContext[i]),
+                                      coordToStr(msgContext[i].line.int),
                                       coordToStr(msgContext[i].col+1)],
                          resetStyle,
-                         getMessageStr(errInstantiationFrom, ""))
+                         instantiationFrom)
     info = msgContext[i]
 
-proc ignoreMsgBecauseOfIdeTools(msg: TMsgKind): bool =
-  msg >= errGenerated and gCmd == cmdIdeTools and optIdeDebug notin gGlobalOptions
+proc ignoreMsgBecauseOfIdeTools(conf: ConfigRef; msg: TMsgKind): bool =
+  msg >= errGenerated and conf.cmd == cmdIdeTools and optIdeDebug notin conf.globalOptions
 
-proc rawMessage*(msg: TMsgKind, args: openArray[string]) =
+proc rawMessage*(conf: ConfigRef; msg: TMsgKind, args: openArray[string]) =
   var
     title: string
     color: ForegroundColor
@@ -928,62 +451,62 @@ proc rawMessage*(msg: TMsgKind, args: openArray[string]) =
   case msg
   of errMin..errMax:
     sev = Severity.Error
-    writeContext(unknownLineInfo())
+    writeContext(conf, unknownLineInfo())
     title = ErrorTitle
     color = ErrorColor
   of warnMin..warnMax:
     sev = Severity.Warning
-    if optWarns notin gOptions: return
-    if msg notin gNotes: return
-    writeContext(unknownLineInfo())
+    if optWarns notin conf.options: return
+    if msg notin conf.notes: return
+    writeContext(conf, unknownLineInfo())
     title = WarningTitle
     color = WarningColor
     kind = WarningsToStr[ord(msg) - ord(warnMin)]
-    inc(gWarnCounter)
+    inc(conf.warnCounter)
   of hintMin..hintMax:
     sev = Severity.Hint
-    if optHints notin gOptions: return
-    if msg notin gNotes: return
+    if optHints notin conf.options: return
+    if msg notin conf.notes: return
     title = HintTitle
     color = HintColor
     if msg != hintUserRaw: kind = HintsToStr[ord(msg) - ord(hintMin)]
-    inc(gHintCounter)
+    inc(conf.hintCounter)
   let s = msgKindToString(msg) % args
 
   if structuredErrorHook != nil:
     structuredErrorHook(unknownLineInfo(), s & (if kind != nil: KindFormat % kind else: ""), sev)
 
-  if not ignoreMsgBecauseOfIdeTools(msg):
+  if not ignoreMsgBecauseOfIdeTools(conf, msg):
     if kind != nil:
       styledMsgWriteln(color, title, resetStyle, s,
                        KindColor, `%`(KindFormat, kind))
     else:
       styledMsgWriteln(color, title, resetStyle, s)
-  handleError(msg, doAbort, s)
+  handleError(conf, msg, doAbort, s)
 
-proc rawMessage*(msg: TMsgKind, arg: string) =
-  rawMessage(msg, [arg])
+proc rawMessage*(conf: ConfigRef; msg: TMsgKind, arg: string) =
+  rawMessage(conf, msg, [arg])
 
-proc resetAttributes* =
-  if {optUseColors, optStdout} * gGlobalOptions == {optUseColors}:
+proc resetAttributes*(conf: ConfigRef) =
+  if {optUseColors, optStdout} * conf.globalOptions == {optUseColors}:
     terminal.resetAttributes(stderr)
 
-proc writeSurroundingSrc(info: TLineInfo) =
+proc writeSurroundingSrc(conf: ConfigRef; info: TLineInfo) =
   const indent = "  "
-  msgWriteln(indent & $info.sourceLine)
-  msgWriteln(indent & spaces(info.col) & '^')
+  msgWriteln(conf, indent & $sourceLine(conf, info))
+  msgWriteln(conf, indent & spaces(info.col) & '^')
 
-proc formatMsg*(info: TLineInfo, msg: TMsgKind, arg: string): string =
+proc formatMsg*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string): string =
   let title = case msg
               of warnMin..warnMax: WarningTitle
               of hintMin..hintMax: HintTitle
               else: ErrorTitle
-  result = PosFormat % [toMsgFilename(info), coordToStr(info.line),
+  result = PosFormat % [toMsgFilename(conf, info), coordToStr(info.line.int),
                         coordToStr(info.col+1)] &
            title &
            getMessageStr(msg, arg)
 
-proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string,
+proc liMessage(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string,
                eh: TErrorHandling) =
   var
     title: string
@@ -994,7 +517,7 @@ proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string,
   case msg
   of errMin..errMax:
     sev = Severity.Error
-    writeContext(info)
+    writeContext(conf, info)
     title = ErrorTitle
     color = ErrorColor
     # we try to filter error messages so that not two error message
@@ -1003,128 +526,124 @@ proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string,
     lastError = info
   of warnMin..warnMax:
     sev = Severity.Warning
-    ignoreMsg = optWarns notin gOptions or msg notin gNotes
-    if not ignoreMsg: writeContext(info)
+    ignoreMsg = optWarns notin conf.options or msg notin conf.notes
+    if not ignoreMsg: writeContext(conf, info)
     title = WarningTitle
     color = WarningColor
     kind = WarningsToStr[ord(msg) - ord(warnMin)]
-    inc(gWarnCounter)
+    inc(conf.warnCounter)
   of hintMin..hintMax:
     sev = Severity.Hint
-    ignoreMsg = optHints notin gOptions or msg notin gNotes
+    ignoreMsg = optHints notin conf.options or msg notin conf.notes
     title = HintTitle
     color = HintColor
     if msg != hintUserRaw: kind = HintsToStr[ord(msg) - ord(hintMin)]
-    inc(gHintCounter)
+    inc(conf.hintCounter)
   # NOTE: currently line info line numbers start with 1,
   # but column numbers start with 0, however most editors expect
   # first column to be 1, so we need to +1 here
-  let x = PosFormat % [toMsgFilename(info), coordToStr(info.line),
+  let x = PosFormat % [toMsgFilename(conf, info), coordToStr(info.line.int),
                        coordToStr(info.col+1)]
   let s = getMessageStr(msg, arg)
 
   if not ignoreMsg:
     if structuredErrorHook != nil:
       structuredErrorHook(info, s & (if kind != nil: KindFormat % kind else: ""), sev)
-    if not ignoreMsgBecauseOfIdeTools(msg):
+    if not ignoreMsgBecauseOfIdeTools(conf, msg):
       if kind != nil:
         styledMsgWriteln(styleBright, x, resetStyle, color, title, resetStyle, s,
                          KindColor, `%`(KindFormat, kind))
       else:
         styledMsgWriteln(styleBright, x, resetStyle, color, title, resetStyle, s)
-      if msg in errMin..errMax and hintSource in gNotes:
-        info.writeSurroundingSrc
-  handleError(msg, eh, s)
+      if hintSource in conf.notes:
+        conf.writeSurroundingSrc(info)
+  handleError(conf, msg, eh, s)
 
-proc fatal*(info: TLineInfo, msg: TMsgKind, arg = "") =
+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}
-  liMessage(info, msg, arg, doAbort)
+  liMessage(conf, info, msg, arg, doAbort)
 
-proc globalError*(info: TLineInfo, msg: TMsgKind, arg = "") =
-  liMessage(info, msg, arg, doRaise)
+proc globalError*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") =
+  liMessage(conf, info, msg, arg, doRaise)
 
-proc globalError*(info: TLineInfo, arg: string) =
-  liMessage(info, errGenerated, arg, doRaise)
+proc globalError*(conf: ConfigRef; info: TLineInfo, arg: string) =
+  liMessage(conf, info, errGenerated, arg, doRaise)
 
-proc localError*(info: TLineInfo, msg: TMsgKind, arg = "") =
-  liMessage(info, msg, arg, doNothing)
+proc localError*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") =
+  liMessage(conf, info, msg, arg, doNothing)
 
-proc localError*(info: TLineInfo, arg: string) =
-  liMessage(info, errGenerated, arg, doNothing)
+proc localError*(conf: ConfigRef; info: TLineInfo, arg: string) =
+  liMessage(conf, info, errGenerated, arg, doNothing)
 
-proc localError*(info: TLineInfo, format: string, params: openarray[string]) =
-  localError(info, format % params)
+proc localError*(conf: ConfigRef; info: TLineInfo, format: string, params: openarray[string]) =
+  localError(conf, info, format % params)
 
-proc message*(info: TLineInfo, msg: TMsgKind, arg = "") =
-  liMessage(info, msg, arg, doNothing)
+proc message*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") =
+  liMessage(conf, info, msg, arg, doNothing)
 
-proc internalError*(info: TLineInfo, errMsg: string) =
-  if gCmd == cmdIdeTools and structuredErrorHook.isNil: return
-  writeContext(info)
-  liMessage(info, errInternal, errMsg, doAbort)
+proc internalError*(conf: ConfigRef; info: TLineInfo, errMsg: string) =
+  if conf.cmd == cmdIdeTools and structuredErrorHook.isNil: return
+  writeContext(conf, info)
+  liMessage(conf, info, errInternal, errMsg, doAbort)
 
-proc internalError*(errMsg: string) =
-  if gCmd == cmdIdeTools and structuredErrorHook.isNil: return
-  writeContext(unknownLineInfo())
-  rawMessage(errInternal, errMsg)
+proc internalError*(conf: ConfigRef; errMsg: string) =
+  if conf.cmd == cmdIdeTools and structuredErrorHook.isNil: return
+  writeContext(conf, unknownLineInfo())
+  rawMessage(conf, errInternal, errMsg)
 
-template assertNotNil*(e): untyped =
-  if e == nil: internalError($instantiationInfo())
+template assertNotNil*(conf: ConfigRef; e): untyped =
+  if e == nil: internalError(conf, $instantiationInfo())
   e
 
-template internalAssert*(e: bool) =
-  if not e: internalError($instantiationInfo())
+template internalAssert*(conf: ConfigRef, e: bool) =
+  if not e: internalError(conf, $instantiationInfo())
 
-proc addSourceLine*(fileIdx: int32, line: string) =
-  fileInfos[fileIdx].lines.add line.rope
+proc addSourceLine*(fileIdx: FileIndex, line: string) =
+  fileInfos[fileIdx.int32].lines.add line.rope
 
-proc sourceLine*(i: TLineInfo): Rope =
-  if i.fileIndex < 0: return nil
+proc sourceLine*(conf: ConfigRef; i: TLineInfo): Rope =
+  if i.fileIndex.int32 < 0: return nil
 
-  if not optPreserveOrigSource and fileInfos[i.fileIndex].lines.len == 0:
+  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
-  internalAssert i.fileIndex < fileInfos.len
+  assert i.fileIndex.int32 < fileInfos.len
   # can happen if the error points to EOF:
-  if i.line > fileInfos[i.fileIndex].lines.len: return nil
+  if i.line.int > fileInfos[i.fileIndex.int32].lines.len: return nil
 
-  result = fileInfos[i.fileIndex].lines[i.line-1]
+  result = fileInfos[i.fileIndex.int32].lines[i.line.int-1]
 
-proc quotedFilename*(i: TLineInfo): Rope =
-  internalAssert i.fileIndex >= 0
-  if optExcessiveStackTrace in gGlobalOptions:
-    result = fileInfos[i.fileIndex].quotedFullName
+proc quotedFilename*(conf: ConfigRef; i: TLineInfo): Rope =
+  assert i.fileIndex.int32 >= 0
+  if optExcessiveStackTrace in conf.globalOptions:
+    result = fileInfos[i.fileIndex.int32].quotedFullName
   else:
-    result = fileInfos[i.fileIndex].quotedName
+    result = fileInfos[i.fileIndex.int32].quotedName
 
 ropes.errorHandler = proc (err: RopesError, msg: string, useWarning: bool) =
   case err
   of rInvalidFormatStr:
-    internalError("ropes: invalid format string: " & msg)
+    internalError(newPartialConfigRef(), "ropes: invalid format string: " & msg)
   of rCannotOpenFile:
-    rawMessage(if useWarning: warnCannotOpenFile else: errCannotOpenFile, msg)
+    rawMessage(newPartialConfigRef(), if useWarning: warnCannotOpenFile else: errCannotOpenFile, msg)
 
-proc listWarnings*() =
-  msgWriteln("Warnings:")
+proc listWarnings*(conf: ConfigRef) =
+  msgWriteln(conf, "Warnings:")
   for warn in warnMin..warnMax:
-    msgWriteln("  [$1] $2" % [
-      if warn in gNotes: "x" else: " ",
-      msgs.WarningsToStr[ord(warn) - ord(warnMin)]
+    msgWriteln(conf, "  [$1] $2" % [
+      if warn in conf.notes: "x" else: " ",
+      configuration.WarningsToStr[ord(warn) - ord(warnMin)]
     ])
 
-proc listHints*() =
-  msgWriteln("Hints:")
+proc listHints*(conf: ConfigRef) =
+  msgWriteln(conf, "Hints:")
   for hint in hintMin..hintMax:
-    msgWriteln("  [$1] $2" % [
-      if hint in gNotes: "x" else: " ",
-      msgs.HintsToStr[ord(hint) - ord(hintMin)]
+    msgWriteln(conf, "  [$1] $2" % [
+      if hint in conf.notes: "x" else: " ",
+      configuration.HintsToStr[ord(hint) - ord(hintMin)]
     ])
-
-# enable colors by default on terminals
-if terminal.isatty(stderr):
-  incl(gGlobalOptions, optUseColors)
diff --git a/compiler/nim.nim b/compiler/nim.nim
index 89225a5e0..d3e00017f 100644
--- a/compiler/nim.nim
+++ b/compiler/nim.nim
@@ -21,7 +21,7 @@ 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
+  nodejs, scriptconfig, idents, modulegraphs, configuration
 
 when hasTinyCBackend:
   import tccgen
@@ -37,77 +37,70 @@ proc prependCurDir(f: string): string =
   else:
     result = f
 
-proc handleCmdLine(cache: IdentCache; config: ConfigRef) =
+proc handleCmdLine(cache: IdentCache; conf: ConfigRef) =
+  condsyms.initDefines(conf.symbols)
   if paramCount() == 0:
-    writeCommandLineUsage()
+    writeCommandLineUsage(conf, conf.helpWritten)
   else:
     # Process command line arguments:
-    processCmdLine(passCmd1, "")
-    if gProjectName == "-":
-      gProjectName = "stdinfile"
-      gProjectFull = "stdinfile"
-      gProjectPath = canonicalizePath getCurrentDir()
-      gProjectIsStdin = true
-    elif gProjectName != "":
+    processCmdLine(passCmd1, "", conf)
+    if conf.projectName == "-":
+      conf.projectName = "stdinfile"
+      conf.projectFull = "stdinfile"
+      conf.projectPath = canonicalizePath(conf, getCurrentDir())
+      conf.projectIsStdin = true
+    elif conf.projectName != "":
       try:
-        gProjectFull = canonicalizePath(gProjectName)
+        conf.projectFull = canonicalizePath(conf, conf.projectName)
       except OSError:
-        gProjectFull = gProjectName
-      let p = splitFile(gProjectFull)
+        conf.projectFull = conf.projectName
+      let p = splitFile(conf.projectFull)
       let dir = if p.dir.len > 0: p.dir else: getCurrentDir()
-      gProjectPath = canonicalizePath dir
-      gProjectName = p.name
+      conf.projectPath = canonicalizePath(conf, dir)
+      conf.projectName = p.name
     else:
-      gProjectPath = canonicalizePath getCurrentDir()
-    loadConfigs(DefaultConfig, config) # load all config files
-    let scriptFile = gProjectFull.changeFileExt("nims")
+      conf.projectPath = canonicalizePath(conf, getCurrentDir())
+    loadConfigs(DefaultConfig, conf) # load all config files
+    let scriptFile = conf.projectFull.changeFileExt("nims")
     if fileExists(scriptFile):
-      runNimScript(cache, scriptFile, freshDefines=false, config)
+      runNimScript(cache, scriptFile, freshDefines=false, conf)
       # 'nim foo.nims' means to just run the NimScript file and do nothing more:
-      if scriptFile == gProjectFull: return
-    elif fileExists(gProjectPath / "config.nims"):
+      if scriptFile == conf.projectFull: return
+    elif fileExists(conf.projectPath / "config.nims"):
       # directory wide NimScript file
-      runNimScript(cache, gProjectPath / "config.nims", freshDefines=false, config)
+      runNimScript(cache, conf.projectPath / "config.nims", freshDefines=false, conf)
     # now process command line arguments again, because some options in the
     # command line can overwite the config file's settings
-    extccomp.initVars()
-    processCmdLine(passCmd2, "")
-    if options.command == "":
-      rawMessage(errNoCommand, command)
-    mainCommand(newModuleGraph(config), cache)
-    if optHints in gOptions and hintGCStats in gNotes: echo(GC_getStatistics())
+    extccomp.initVars(conf)
+    processCmdLine(passCmd2, "", conf)
+    if conf.command == "":
+      rawMessage(conf, errGenerated, "command missing")
+    mainCommand(newModuleGraph(conf), cache)
+    if optHints in conf.options and hintGCStats in conf.notes: echo(GC_getStatistics())
     #echo(GC_getStatistics())
-    if msgs.gErrorCounter == 0:
+    if conf.errorCounter == 0:
       when hasTinyCBackend:
-        if gCmd == cmdRun:
-          tccgen.run(commands.arguments)
-      if optRun in gGlobalOptions:
-        if gCmd == cmdCompileToJS:
+        if conf.cmd == cmdRun:
+          tccgen.run(conf.arguments)
+      if optRun in conf.globalOptions:
+        if conf.cmd == cmdCompileToJS:
           var ex: string
-          if options.outFile.len > 0:
-            ex = options.outFile.prependCurDir.quoteShell
+          if conf.outFile.len > 0:
+            ex = conf.outFile.prependCurDir.quoteShell
           else:
             ex = quoteShell(
-              completeCFilePath(changeFileExt(gProjectFull, "js").prependCurDir))
-          execExternalProgram(findNodeJs() & " " & ex & ' ' & commands.arguments)
-        elif gCmd == cmdCompileToPHP:
-          var ex: string
-          if options.outFile.len > 0:
-            ex = options.outFile.prependCurDir.quoteShell
-          else:
-            ex = quoteShell(
-              completeCFilePath(changeFileExt(gProjectFull, "php").prependCurDir))
-          execExternalProgram("php " & ex & ' ' & commands.arguments)
+              completeCFilePath(conf, changeFileExt(conf.projectFull, "js").prependCurDir))
+          execExternalProgram(conf, findNodeJs() & " " & ex & ' ' & conf.arguments)
         else:
           var binPath: string
-          if options.outFile.len > 0:
+          if conf.outFile.len > 0:
             # If the user specified an outFile path, use that directly.
-            binPath = options.outFile.prependCurDir
+            binPath = conf.outFile.prependCurDir
           else:
             # Figure out ourselves a valid binary name.
-            binPath = changeFileExt(gProjectFull, ExeExt).prependCurDir
+            binPath = changeFileExt(conf.projectFull, ExeExt).prependCurDir
           var ex = quoteShell(binPath)
-          execExternalProgram(ex & ' ' & commands.arguments)
+          execExternalProgram(conf, ex & ' ' & conf.arguments)
 
 when declared(GC_setMaxPause):
   GC_setMaxPause 2_000
@@ -115,10 +108,10 @@ when declared(GC_setMaxPause):
 when compileOption("gc", "v2") or compileOption("gc", "refc"):
   # the new correct mark&sweet collector is too slow :-/
   GC_disableMarkAndSweep()
-condsyms.initDefines()
 
 when not defined(selftest):
-  handleCmdLine(newIdentCache(), newConfigRef())
+  let conf = newConfigRef()
+  handleCmdLine(newIdentCache(), conf)
   when declared(GC_setMaxPause):
     echo GC_getStatistics()
-  msgQuit(int8(msgs.gErrorCounter > 0))
+  msgQuit(int8(conf.errorCounter > 0))
diff --git a/compiler/nimblecmd.nim b/compiler/nimblecmd.nim
index 0f9e03352..8305b01f5 100644
--- a/compiler/nimblecmd.nim
+++ b/compiler/nimblecmd.nim
@@ -9,11 +9,12 @@
 
 ## Implements some helper procs for Nimble (Nim's package manager) support.
 
-import parseutils, strutils, strtabs, os, options, msgs, sequtils
+import parseutils, strutils, strtabs, os, options, msgs, sequtils,
+  configuration
 
-proc addPath*(path: string, info: TLineInfo) =
-  if not options.searchPaths.contains(path):
-    options.searchPaths.insert(path, 0)
+proc addPath*(conf: ConfigRef; path: string, info: TLineInfo) =
+  if not conf.searchPaths.contains(path):
+    conf.searchPaths.insert(path, 0)
 
 type
   Version* = distinct string
@@ -84,7 +85,7 @@ proc getPathVersion*(p: string): tuple[name, version: string] =
   result.name = p[0 .. sepIdx - 1]
   result.version = p.substr(sepIdx + 1)
 
-proc addPackage(packages: StringTableRef, p: string; info: TLineInfo) =
+proc addPackage(conf: ConfigRef; packages: StringTableRef, p: string; info: TLineInfo) =
   let (name, ver) = getPathVersion(p)
   if isValidVersion(ver):
     let version = newVersion(ver)
@@ -92,14 +93,14 @@ proc addPackage(packages: StringTableRef, p: string; info: TLineInfo) =
       (not packages.hasKey(name)):
       packages[name] = $version
   else:
-    localError(info, "invalid package name: " & p)
+    localError(conf, info, "invalid package name: " & p)
 
 iterator chosen(packages: StringTableRef): string =
   for key, val in pairs(packages):
     let res = if val.len == 0: key else: key & '-' & val
     yield res
 
-proc addNimblePath(p: string, info: TLineInfo) =
+proc addNimblePath(conf: ConfigRef; p: string, info: TLineInfo) =
   var path = p
   let nimbleLinks = toSeq(walkPattern(p / "*.nimble-link"))
   if nimbleLinks.len > 0:
@@ -111,23 +112,23 @@ proc addNimblePath(p: string, info: TLineInfo) =
     if not path.isAbsolute():
       path = p / path
 
-  if not contains(options.searchPaths, path):
-    message(info, hintPath, path)
-    options.lazyPaths.insert(path, 0)
+  if not contains(conf.searchPaths, path):
+    message(conf, info, hintPath, path)
+    conf.lazyPaths.insert(path, 0)
 
-proc addPathRec(dir: string, info: TLineInfo) =
+proc addPathRec(conf: ConfigRef; dir: string, info: TLineInfo) =
   var packages = newStringTable(modeStyleInsensitive)
   var pos = dir.len-1
   if dir[pos] in {DirSep, AltSep}: inc(pos)
   for k,p in os.walkDir(dir):
     if k == pcDir and p[pos] != '.':
-      addPackage(packages, p, info)
+      addPackage(conf, packages, p, info)
   for p in packages.chosen:
-    addNimblePath(p, info)
+    addNimblePath(conf, p, info)
 
-proc nimblePath*(path: string, info: TLineInfo) =
-  addPathRec(path, info)
-  addNimblePath(path, info)
+proc nimblePath*(conf: ConfigRef; path: string, info: TLineInfo) =
+  addPathRec(conf, path, info)
+  addNimblePath(conf, path, info)
 
 when isMainModule:
   proc v(s: string): Version = s.newVersion
diff --git a/compiler/nimconf.nim b/compiler/nimconf.nim
index c19b41af1..6cb5bab0f 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
+  options, idents, wordrecg, strtabs, configuration
 
 # ---------------- configuration file parser -----------------------------
 # we use Nim's scanner here to save space and work
@@ -27,12 +27,12 @@ proc parseAtom(L: var TLexer, tok: var TToken; config: ConfigRef): bool =
     ppGetTok(L, tok)
     result = parseExpr(L, tok, config)
     if tok.tokType == tkParRi: ppGetTok(L, tok)
-    else: lexMessage(L, errTokenExpected, "\')\'")
+    else: lexMessage(L, errGenerated, "expected closing ')'")
   elif tok.ident.id == ord(wNot):
     ppGetTok(L, tok)
     result = not parseAtom(L, tok, config)
   else:
-    result = isDefined(tok.ident)
+    result = isDefined(config, tok.ident.s)
     ppGetTok(L, tok)
 
 proc parseAndExpr(L: var TLexer, tok: var TToken; config: ConfigRef): bool =
@@ -53,12 +53,12 @@ proc evalppIf(L: var TLexer, tok: var TToken; config: ConfigRef): bool =
   ppGetTok(L, tok)            # skip 'if' or 'elif'
   result = parseExpr(L, tok, config)
   if tok.tokType == tkColon: ppGetTok(L, tok)
-  else: lexMessage(L, errTokenExpected, "\':\'")
+  else: lexMessage(L, errGenerated, "expected ':'")
 
-var condStack: seq[bool] = @[]
+#var condStack: seq[bool] = @[]
 
-proc doEnd(L: var TLexer, tok: var TToken) =
-  if high(condStack) < 0: lexMessage(L, errTokenExpected, "@if")
+proc doEnd(L: var TLexer, tok: var TToken; condStack: var seq[bool]) =
+  if high(condStack) < 0: lexMessage(L, errGenerated, "expected @if")
   ppGetTok(L, tok)            # skip 'end'
   setLen(condStack, high(condStack))
 
@@ -66,20 +66,22 @@ type
   TJumpDest = enum
     jdEndif, jdElseEndif
 
-proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest; config: ConfigRef)
-proc doElse(L: var TLexer, tok: var TToken; config: ConfigRef) =
-  if high(condStack) < 0: lexMessage(L, errTokenExpected, "@if")
+proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest; config: ConfigRef;
+                     condStack: var seq[bool])
+proc doElse(L: var TLexer, tok: var TToken; config: ConfigRef; condStack: var seq[bool]) =
+  if high(condStack) < 0: lexMessage(L, errGenerated, "expected @if")
   ppGetTok(L, tok)
   if tok.tokType == tkColon: ppGetTok(L, tok)
-  if condStack[high(condStack)]: jumpToDirective(L, tok, jdEndif, config)
+  if condStack[high(condStack)]: jumpToDirective(L, tok, jdEndif, config, condStack)
 
-proc doElif(L: var TLexer, tok: var TToken; config: ConfigRef) =
-  if high(condStack) < 0: lexMessage(L, errTokenExpected, "@if")
+proc doElif(L: var TLexer, tok: var TToken; config: ConfigRef; condStack: var seq[bool]) =
+  if high(condStack) < 0: lexMessage(L, errGenerated, "expected @if")
   var res = evalppIf(L, tok, config)
-  if condStack[high(condStack)] or not res: jumpToDirective(L, tok, jdElseEndif, config)
+  if condStack[high(condStack)] or not res: jumpToDirective(L, tok, jdElseEndif, config, condStack)
   else: condStack[high(condStack)] = true
 
-proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest; config: ConfigRef) =
+proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest; config: ConfigRef;
+                     condStack: var seq[bool]) =
   var nestedIfs = 0
   while true:
     if tok.ident != nil and tok.ident.s == "@":
@@ -89,39 +91,39 @@ proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest; config: Co
         inc(nestedIfs)
       of wElse:
         if dest == jdElseEndif and nestedIfs == 0:
-          doElse(L, tok, config)
+          doElse(L, tok, config, condStack)
           break
       of wElif:
         if dest == jdElseEndif and nestedIfs == 0:
-          doElif(L, tok, config)
+          doElif(L, tok, config, condStack)
           break
       of wEnd:
         if nestedIfs == 0:
-          doEnd(L, tok)
+          doEnd(L, tok, condStack)
           break
         if nestedIfs > 0: dec(nestedIfs)
       else:
         discard
       ppGetTok(L, tok)
     elif tok.tokType == tkEof:
-      lexMessage(L, errTokenExpected, "@end")
+      lexMessage(L, errGenerated, "expected @end")
     else:
       ppGetTok(L, tok)
 
-proc parseDirective(L: var TLexer, tok: var TToken; config: ConfigRef) =
+proc parseDirective(L: var TLexer, tok: var TToken; config: ConfigRef; condStack: var seq[bool]) =
   ppGetTok(L, tok)            # skip @
   case whichKeyword(tok.ident)
   of wIf:
     setLen(condStack, len(condStack) + 1)
     let res = evalppIf(L, tok, config)
     condStack[high(condStack)] = res
-    if not res: jumpToDirective(L, tok, jdElseEndif, config)
-  of wElif: doElif(L, tok, config)
-  of wElse: doElse(L, tok, config)
-  of wEnd: doEnd(L, tok)
+    if not res: jumpToDirective(L, tok, jdElseEndif, config, condStack)
+  of wElif: doElif(L, tok, config, condStack)
+  of wElse: doElse(L, tok, config, condStack)
+  of wEnd: doEnd(L, tok, condStack)
   of wWrite:
     ppGetTok(L, tok)
-    msgs.msgWriteln(strtabs.`%`(tokToStr(tok), options.gConfigVars,
+    msgs.msgWriteln(config, strtabs.`%`(tokToStr(tok), config.configVars,
                                 {useEnvironment, useKey}))
     ppGetTok(L, tok)
   else:
@@ -144,55 +146,57 @@ proc parseDirective(L: var TLexer, tok: var TToken; config: ConfigRef) =
       ppGetTok(L, tok)
       os.putEnv(key, os.getEnv(key) & tokToStr(tok))
       ppGetTok(L, tok)
-    else: lexMessage(L, errInvalidDirectiveX, tokToStr(tok))
+    else:
+      lexMessage(L, errGenerated, "invalid directive: '$1'" % tokToStr(tok))
 
-proc confTok(L: var TLexer, tok: var TToken; config: ConfigRef) =
+proc confTok(L: var TLexer, tok: var TToken; config: ConfigRef; condStack: var seq[bool]) =
   ppGetTok(L, tok)
   while tok.ident != nil and tok.ident.s == "@":
-    parseDirective(L, tok, config)    # else: give the token to the parser
+    parseDirective(L, tok, config, condStack)    # else: give the token to the parser
 
 proc checkSymbol(L: TLexer, tok: TToken) =
   if tok.tokType notin {tkSymbol..tkInt64Lit, tkStrLit..tkTripleStrLit}:
-    lexMessage(L, errIdentifierExpected, tokToStr(tok))
+    lexMessage(L, errGenerated, "expected identifier, but got: " & tokToStr(tok))
 
-proc parseAssignment(L: var TLexer, tok: var TToken; config: ConfigRef) =
+proc parseAssignment(L: var TLexer, tok: var TToken;
+                     config: ConfigRef; condStack: var seq[bool]) =
   if tok.ident.s == "-" or tok.ident.s == "--":
-    confTok(L, tok, config)           # skip unnecessary prefix
+    confTok(L, tok, config, condStack)           # skip unnecessary prefix
   var info = getLineInfo(L, tok) # save for later in case of an error
   checkSymbol(L, tok)
   var s = tokToStr(tok)
-  confTok(L, tok, config)             # skip symbol
+  confTok(L, tok, config, condStack)             # skip symbol
   var val = ""
   while tok.tokType == tkDot:
     add(s, '.')
-    confTok(L, tok, config)
+    confTok(L, tok, config, condStack)
     checkSymbol(L, tok)
     add(s, tokToStr(tok))
-    confTok(L, tok, config)
+    confTok(L, tok, config, condStack)
   if tok.tokType == tkBracketLe:
     # BUGFIX: val, not s!
     # BUGFIX: do not copy '['!
-    confTok(L, tok, config)
+    confTok(L, tok, config, condStack)
     checkSymbol(L, tok)
     add(val, tokToStr(tok))
-    confTok(L, tok, config)
-    if tok.tokType == tkBracketRi: confTok(L, tok, config)
-    else: lexMessage(L, errTokenExpected, "']'")
+    confTok(L, tok, config, condStack)
+    if tok.tokType == tkBracketRi: confTok(L, tok, config, condStack)
+    else: lexMessage(L, errGenerated, "expected closing ']'")
     add(val, ']')
   let percent = tok.ident != nil and tok.ident.s == "%="
   if tok.tokType in {tkColon, tkEquals} or percent:
     if len(val) > 0: add(val, ':')
-    confTok(L, tok, config)           # skip ':' or '=' or '%'
+    confTok(L, tok, config, condStack)           # skip ':' or '=' or '%'
     checkSymbol(L, tok)
     add(val, tokToStr(tok))
-    confTok(L, tok, config)           # skip symbol
+    confTok(L, tok, config, condStack)           # skip symbol
     while tok.ident != nil and tok.ident.s == "&":
-      confTok(L, tok, config)
+      confTok(L, tok, config, condStack)
       checkSymbol(L, tok)
       add(val, tokToStr(tok))
-      confTok(L, tok, config)
+      confTok(L, tok, config, condStack)
   if percent:
-    processSwitch(s, strtabs.`%`(val, options.gConfigVars,
+    processSwitch(s, strtabs.`%`(val, config.configVars,
                                 {useEnvironment, useEmpty}), passPP, info, config)
   else:
     processSwitch(s, val, passPP, info, config)
@@ -205,54 +209,51 @@ proc readConfigFile(filename: string; cache: IdentCache; config: ConfigRef) =
   stream = llStreamOpen(filename, fmRead)
   if stream != nil:
     initToken(tok)
-    openLexer(L, filename, stream, cache)
+    openLexer(L, filename, stream, cache, config)
     tok.tokType = tkEof       # to avoid a pointless warning
-    confTok(L, tok, config)           # read in the first token
-    while tok.tokType != tkEof: parseAssignment(L, tok, config)
-    if len(condStack) > 0: lexMessage(L, errTokenExpected, "@end")
+    var condStack: seq[bool] = @[]
+    confTok(L, tok, config, condStack)           # read in the first token
+    while tok.tokType != tkEof: parseAssignment(L, tok, config, condStack)
+    if len(condStack) > 0: lexMessage(L, errGenerated, "expected @end")
     closeLexer(L)
-    rawMessage(hintConf, filename)
+    rawMessage(config, hintConf, filename)
 
 proc getUserConfigPath(filename: string): string =
   result = joinPath(getConfigDir(), filename)
 
-proc getSystemConfigPath(filename: string): string =
+proc getSystemConfigPath(conf: ConfigRef; filename: string): string =
   # try standard configuration file (installation did not distribute files
   # the UNIX way)
-  let p = getPrefixDir()
+  let p = getPrefixDir(conf)
   result = joinPath([p, "config", filename])
   when defined(unix):
     if not existsFile(result): result = joinPath([p, "etc", filename])
     if not existsFile(result): result = "/etc/" & filename
 
-proc loadConfigs*(cfg: string; cache: IdentCache; config: ConfigRef = nil) =
-  setDefaultLibpath()
+proc loadConfigs*(cfg: string; cache: IdentCache; conf: ConfigRef) =
+  setDefaultLibpath(conf)
 
-  if optSkipConfigFile notin gGlobalOptions:
-    readConfigFile(getSystemConfigPath(cfg), cache, config)
+  if optSkipConfigFile notin conf.globalOptions:
+    readConfigFile(getSystemConfigPath(conf, cfg), cache, conf)
 
-  if optSkipUserConfigFile notin gGlobalOptions:
-    readConfigFile(getUserConfigPath(cfg), cache, config)
+  if optSkipUserConfigFile notin conf.globalOptions:
+    readConfigFile(getUserConfigPath(cfg), cache, conf)
 
-  var pd = if gProjectPath.len > 0: gProjectPath else: getCurrentDir()
-  if optSkipParentConfigFiles notin gGlobalOptions:
+  let pd = if conf.projectPath.len > 0: conf.projectPath else: getCurrentDir()
+  if optSkipParentConfigFiles notin conf.globalOptions:
     for dir in parentDirs(pd, fromRoot=true, inclusive=false):
-      readConfigFile(dir / cfg, cache, config)
+      readConfigFile(dir / cfg, cache, conf)
 
-  if optSkipProjConfigFile notin gGlobalOptions:
-    readConfigFile(pd / cfg, cache, config)
+  if optSkipProjConfigFile notin conf.globalOptions:
+    readConfigFile(pd / cfg, cache, conf)
 
-    if gProjectName.len != 0:
+    if conf.projectName.len != 0:
       # new project wide config file:
-      var projectConfig = changeFileExt(gProjectFull, "nimcfg")
-      if not fileExists(projectConfig):
-        projectConfig = changeFileExt(gProjectFull, "nim.cfg")
+      var projectConfig = changeFileExt(conf.projectFull, "nimcfg")
       if not fileExists(projectConfig):
-        projectConfig = changeFileExt(gProjectFull, "nimrod.cfg")
-        if fileExists(projectConfig):
-          rawMessage(warnDeprecated, projectConfig)
-      readConfigFile(projectConfig, cache, config)
+        projectConfig = changeFileExt(conf.projectFull, "nim.cfg")
+      readConfigFile(projectConfig, cache, conf)
 
-proc loadConfigs*(cfg: string; config: ConfigRef = nil) =
+proc loadConfigs*(cfg: string; conf: ConfigRef) =
   # for backwards compatibility only.
-  loadConfigs(cfg, newIdentCache(), config)
+  loadConfigs(cfg, newIdentCache(), conf)
diff --git a/compiler/nimfix/nimfix.nim b/compiler/nimfix/nimfix.nim
index a97d88078..c3e29f9a1 100644
--- a/compiler/nimfix/nimfix.nim
+++ b/compiler/nimfix/nimfix.nim
@@ -38,7 +38,7 @@ In addition, all command line options of Nim are supported.
 proc mainCommand =
   registerPass verbosePass
   registerPass semPass
-  gCmd = cmdPretty
+  conf.cmd = cmdPretty
   searchPaths.add options.libpath
   if gProjectFull.len != 0:
     # current path is always looked first for modules
@@ -47,7 +47,7 @@ proc mainCommand =
   compileProject(newModuleGraph(), newIdentCache())
   pretty.overwriteFiles()
 
-proc processCmdLine*(pass: TCmdLinePass, cmd: string) =
+proc processCmdLine*(pass: TCmdLinePass, cmd: string, config: ConfigRef) =
   var p = parseopt.initOptParser(cmd)
   var argsCount = 0
   gOnlyMainfile = true
@@ -76,16 +76,16 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string) =
       of "wholeproject": gOnlyMainfile = false
       of "besteffort": msgs.gErrorMax = high(int) # don't stop after first error
       else:
-        processSwitch(pass, p)
+        processSwitch(pass, p, config)
     of cmdArgument:
       options.gProjectName = unixToNativePath(p.key)
       # if processArgument(pass, p, argsCount): break
 
-proc handleCmdLine() =
+proc handleCmdLine(config: ConfigRef) =
   if paramCount() == 0:
     stdout.writeLine(Usage)
   else:
-    processCmdLine(passCmd1, "")
+    processCmdLine(passCmd1, "", config)
     if gProjectName != "":
       try:
         gProjectFull = canonicalizePath(gProjectName)
@@ -96,11 +96,11 @@ proc handleCmdLine() =
       gProjectName = p.name
     else:
       gProjectPath = getCurrentDir()
-    loadConfigs(DefaultConfig) # load all config files
+    loadConfigs(DefaultConfig, config) # load all config files
     # now process command line arguments again, because some options in the
     # command line can overwite the config file's settings
     extccomp.initVars()
-    processCmdLine(passCmd2, "")
+    processCmdLine(passCmd2, "", config)
     mainCommand()
 
 when compileOption("gc", "v2") or compileOption("gc", "refc"):
@@ -108,4 +108,4 @@ when compileOption("gc", "v2") or compileOption("gc", "refc"):
 
 condsyms.initDefines()
 defineSymbol "nimfix"
-handleCmdline()
+handleCmdline newConfigRef()
diff --git a/compiler/nimfix/pretty.nim b/compiler/nimfix/pretty.nim
index 55603f4cd..96429ad53 100644
--- a/compiler/nimfix/pretty.nim
+++ b/compiler/nimfix/pretty.nim
@@ -13,7 +13,8 @@
 import
   strutils, os, intsets, strtabs
 
-import "../compiler" / [options, ast, astalgo, msgs, semdata, ropes, idents]
+import ".." / [options, ast, astalgo, msgs, semdata, ropes, idents,
+  configuration]
 import prettybase
 
 type
@@ -24,11 +25,11 @@ var
   gStyleCheck*: StyleCheck
   gCheckExtern*, gOnlyMainfile*: bool
 
-proc overwriteFiles*() =
-  let doStrip = options.getConfigVar("pretty.strip").normalize == "on"
+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 == gProjectMainIdx):
+        (not gOnlyMainfile or gSourceFiles[i].fileIdx == conf.projectMainIdx.FileIndex):
       let newFile = if gOverWrite: gSourceFiles[i].fullpath
                     else: gSourceFiles[i].fullpath.changeFileExt(".pretty.nim")
       try:
@@ -41,7 +42,7 @@ proc overwriteFiles*() =
           f.write(gSourceFiles[i].newline)
         f.close
       except IOError:
-        rawMessage(errCannotOpenFile, newFile)
+        rawMessage(conf, errGenerated, "cannot open file: " & newFile)
 
 proc `=~`(s: string, a: openArray[string]): bool =
   for x in a:
@@ -95,7 +96,7 @@ proc beautifyName(s: string, k: TSymKind): string =
 proc replaceInFile(info: TLineInfo; newName: string) =
   loadFile(info)
 
-  let line = gSourceFiles[info.fileIndex].lines[info.line-1]
+  let line = gSourceFiles[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)
@@ -107,28 +108,28 @@ 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].lines[info.line-1], x)
-    gSourceFiles[info.fileIndex].dirty = true
+    system.shallowCopy(gSourceFiles[info.fileIndex.int].lines[info.line.int-1], x)
+    gSourceFiles[info.fileIndex.int].dirty = true
 
-proc checkStyle(info: TLineInfo, s: string, k: TSymKind; sym: PSym) =
+proc checkStyle(conf: ConfigRef; 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)
     else:
-      message(info, hintName, beau)
+      message(conf, info, hintName, beau)
 
-proc styleCheckDefImpl(info: TLineInfo; s: PSym; k: TSymKind) =
+proc styleCheckDefImpl(conf: ConfigRef; 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 {skType, skGenericParam} and sfAnon in s.flags: return
   if {sfImportc, sfExportc} * s.flags == {} or gCheckExtern:
-    checkStyle(info, s.name.s, k, s)
+    checkStyle(conf, info, s.name.s, k, s)
 
 template styleCheckDef*(info: TLineInfo; s: PSym; k: TSymKind) =
   when defined(nimfix):
-    if gStyleCheck != StyleCheck.None: styleCheckDefImpl(info, s, k)
+    if gStyleCheck != StyleCheck.None: styleCheckDefImpl(conf, info, s, k)
 
 template styleCheckDef*(info: TLineInfo; s: PSym) =
   styleCheckDef(info, s, s.kind)
@@ -136,7 +137,7 @@ template styleCheckDef*(s: PSym) =
   styleCheckDef(s.info, s, s.kind)
 
 proc styleCheckUseImpl(info: TLineInfo; s: PSym) =
-  if info.fileIndex < 0: return
+  if info.fileIndex.int < 0: return
   # we simply convert it to what it looks like in the definition
   # for consistency
 
@@ -151,4 +152,4 @@ proc styleCheckUseImpl(info: TLineInfo; s: PSym) =
 
 template styleCheckUse*(info: TLineInfo; s: PSym) =
   when defined(nimfix):
-    if gStyleCheck != StyleCheck.None: styleCheckUseImpl(info, s)
+    if gStyleCheck != StyleCheck.None: styleCheckUseImpl(conf, info, s)
diff --git a/compiler/nimfix/prettybase.nim b/compiler/nimfix/prettybase.nim
index f1d24183b..c32dbe623 100644
--- a/compiler/nimfix/prettybase.nim
+++ b/compiler/nimfix/prettybase.nim
@@ -8,7 +8,7 @@
 #
 
 import strutils, lexbase, streams
-import "../compiler" / [ast, msgs, idents]
+import ".." / [ast, msgs, idents]
 from os import splitFile
 
 type
@@ -16,13 +16,13 @@ type
     lines*: seq[string]
     dirty*, isNimfixFile*: bool
     fullpath*, newline*: string
-    fileIdx*: int32
+    fileIdx*: FileIndex
 
 var
   gSourceFiles*: seq[TSourceFile] = @[]
 
 proc loadFile*(info: TLineInfo) =
-  let i = info.fileIndex
+  let i = info.fileIndex.int
   if i >= gSourceFiles.len:
     gSourceFiles.setLen(i+1)
   if gSourceFiles[i].lines.isNil:
@@ -64,7 +64,7 @@ proc differ*(line: string, a, b: int, x: string): bool =
 proc replaceDeprecated*(info: TLineInfo; oldSym, newSym: PIdent) =
   loadFile(info)
 
-  let line = gSourceFiles[info.fileIndex].lines[info.line-1]
+  let line = gSourceFiles[info.fileIndex.int32].lines[info.line.int-1]
   var first = min(info.col.int, line.len)
   if first < 0: return
   #inc first, skipIgnoreCase(line, "proc ", first)
@@ -75,8 +75,8 @@ 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].lines[info.line-1], x)
-    gSourceFiles[info.fileIndex].dirty = true
+    system.shallowCopy(gSourceFiles[info.fileIndex.int32].lines[info.line.int-1], x)
+    gSourceFiles[info.fileIndex.int32].dirty = true
     #if newSym.s == "File": writeStackTrace()
 
 proc replaceDeprecated*(info: TLineInfo; oldSym, newSym: PSym) =
@@ -85,10 +85,10 @@ proc replaceDeprecated*(info: TLineInfo; oldSym, newSym: PSym) =
 proc replaceComment*(info: TLineInfo) =
   loadFile(info)
 
-  let line = gSourceFiles[info.fileIndex].lines[info.line-1]
+  let line = gSourceFiles[info.fileIndex.int32].lines[info.line.int-1]
   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].lines[info.line-1], x)
-  gSourceFiles[info.fileIndex].dirty = true
+  system.shallowCopy(gSourceFiles[info.fileIndex.int32].lines[info.line.int-1], x)
+  gSourceFiles[info.fileIndex.int32].dirty = true
diff --git a/compiler/nimsets.nim b/compiler/nimsets.nim
index 8ec4d3c0d..6cb675ed8 100644
--- a/compiler/nimsets.nim
+++ b/compiler/nimsets.nim
@@ -12,27 +12,10 @@
 import
   ast, astalgo, trees, nversion, msgs, platform, bitsets, types, renderer
 
-proc toBitSet*(s: PNode, b: var TBitSet)
-  # this function is used for case statement checking:
-proc overlap*(a, b: PNode): bool
-proc inSet*(s: PNode, elem: PNode): bool
-proc someInSet*(s: PNode, a, b: PNode): bool
-proc emptyRange*(a, b: PNode): bool
-proc setHasRange*(s: PNode): bool
-  # returns true if set contains a range (needed by the code generator)
-  # these are used for constant folding:
-proc unionSets*(a, b: PNode): PNode
-proc diffSets*(a, b: PNode): PNode
-proc intersectSets*(a, b: PNode): PNode
-proc symdiffSets*(a, b: PNode): PNode
-proc containsSets*(a, b: PNode): bool
-proc equalSets*(a, b: PNode): bool
-proc cardSet*(a: PNode): BiggestInt
-# implementation
-
-proc inSet(s: PNode, elem: PNode): bool =
+proc inSet*(s: PNode, elem: PNode): bool =
+  assert s.kind == nkCurly
   if s.kind != nkCurly:
-    internalError(s.info, "inSet")
+    #internalError(s.info, "inSet")
     return false
   for i in countup(0, sonsLen(s) - 1):
     if s.sons[i].kind == nkRange:
@@ -44,7 +27,7 @@ proc inSet(s: PNode, elem: PNode): bool =
         return true
   result = false
 
-proc overlap(a, b: PNode): bool =
+proc overlap*(a, b: PNode): bool =
   if a.kind == nkRange:
     if b.kind == nkRange:
       # X..Y and C..D overlap iff (X <= D and C <= Y)
@@ -58,10 +41,11 @@ proc overlap(a, b: PNode): bool =
     else:
       result = sameValue(a, b)
 
-proc someInSet(s: PNode, a, b: PNode): bool =
+proc someInSet*(s: PNode, a, b: PNode): bool =
   # checks if some element of a..b is in the set s
+  assert s.kind == nkCurly
   if s.kind != nkCurly:
-    internalError(s.info, "SomeInSet")
+    #internalError(s.info, "SomeInSet")
     return false
   for i in countup(0, sonsLen(s) - 1):
     if s.sons[i].kind == nkRange:
@@ -74,7 +58,7 @@ proc someInSet(s: PNode, a, b: PNode): bool =
         return true
   result = false
 
-proc toBitSet(s: PNode, b: var TBitSet) =
+proc toBitSet*(s: PNode, b: var TBitSet) =
   var first, j: BiggestInt
   first = firstOrd(s.typ.sons[0])
   bitSetInit(b, int(getSize(s.typ)))
@@ -87,7 +71,7 @@ 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*(s: TBitSet, settype: PType, info: TLineInfo): PNode =
   var
     a, b, e, first: BiggestInt # a, b are interval borders
     elemType: PType
@@ -128,18 +112,18 @@ template nodeSetOp(a, b: PNode, op: untyped) {.dirty.} =
   op(x, y)
   result = toTreeSet(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*(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 containsSets(a, b: PNode): bool =
+proc containsSets*(a, b: PNode): bool =
   var x, y: TBitSet
   toBitSet(a, x)
   toBitSet(b, y)
   result = bitSetContains(x, y)
 
-proc equalSets(a, b: PNode): bool =
+proc equalSets*(a, b: PNode): bool =
   var x, y: TBitSet
   toBitSet(a, x)
   toBitSet(b, y)
@@ -156,20 +140,19 @@ proc deduplicate*(a: PNode): PNode =
   toBitSet(a, x)
   result = toTreeSet(x, a.typ, a.info)
 
-proc cardSet(a: PNode): BiggestInt =
+proc cardSet*(a: PNode): BiggestInt =
   var x: TBitSet
   toBitSet(a, x)
   result = bitSetCard(x)
 
-proc setHasRange(s: PNode): bool =
+proc setHasRange*(s: PNode): bool =
+  assert s.kind == nkCurly
   if s.kind != nkCurly:
-    internalError(s.info, "SetHasRange")
     return false
   for i in countup(0, sonsLen(s) - 1):
     if s.sons[i].kind == nkRange:
       return true
   result = false
 
-proc emptyRange(a, b: PNode): bool =
+proc emptyRange*(a, b: PNode): bool =
   result = not leValue(a, b)  # a > b iff not (a <= b)
-
diff --git a/compiler/nversion.nim b/compiler/nversion.nim
index 85265a7c0..caa818d79 100644
--- a/compiler/nversion.nim
+++ b/compiler/nversion.nim
@@ -15,3 +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
+                             ## as older versions of the compiler API do not
+                             ## declare this.
diff --git a/compiler/options.nim b/compiler/options.nim
index 69a555b3f..2027897fa 100644
--- a/compiler/options.nim
+++ b/compiler/options.nim
@@ -8,7 +8,9 @@
 #
 
 import
-  os, strutils, strtabs, osproc, sets
+  os, strutils, strtabs, osproc, sets, configuration, platform
+
+from terminal import isatty
 
 const
   hasTinyCBackend* = defined(tinyc)
@@ -17,7 +19,6 @@ const
   hasFFI* = defined(useFFI)
   newScopeForIf* = true
   useCaas* = not defined(noCaas)
-  noTimeMachine* = defined(avoidTimeMachine) and defined(macosx)
   copyrightYear* = "2018"
 
 type                          # please make sure we have under 32 options
@@ -37,11 +38,13 @@ type                          # please make sure we have under 32 options
                               # evaluation
     optPatterns,              # en/disable pattern matching
     optMemTracker,
-    optHotCodeReloading
+    optHotCodeReloading,
+    optLaxStrings
 
   TOptions* = set[TOption]
   TGlobalOption* = enum       # **keep binary compatible**
-    gloptNone, optForceFullMake, optDeadCodeElim,
+    gloptNone, optForceFullMake,
+    optDeadCodeElimUnused,    # deprecated, always on
     optListCmd, optCompileOnly, optNoLinking,
     optCDebug,                # turn on debugging information
     optGenDynLib,             # generate a dynamic library
@@ -69,6 +72,11 @@ type                          # please make sure we have under 32 options
     optIdeTerse               # idetools: use terse descriptions
     optNoCppExceptions        # use C exception handling even with CPP
     optExcessiveStackTrace    # fully qualified module filenames
+    optWholeProject           # for 'doc2': output any dependency
+    optListFullPaths
+    optNoNimblePath
+    optDynlibOverrideAll
+    optUseNimNamespace
 
   TGlobalOptions* = set[TGlobalOption]
 
@@ -81,7 +89,6 @@ type
                               # **keep binary compatible**
     cmdNone, cmdCompileToC, cmdCompileToCpp, cmdCompileToOC,
     cmdCompileToJS,
-    cmdCompileToPHP,
     cmdCompileToLLVM, cmdInterpret, cmdPretty, cmdDoc,
     cmdGenDepend, cmdDump,
     cmdCheck,                 # semantic checking for whole project
@@ -103,70 +110,185 @@ type
     ideNone, ideSug, ideCon, ideDef, ideUse, ideDus, ideChk, ideMod,
     ideHighlight, ideOutline, ideKnown, ideMsg
 
-  ConfigRef* = ref object ## eventually all global configuration should be moved here
-    cppDefines*: HashSet[string]
-    headerFile*: string
+  Feature* = enum  ## experimental features
+    implicitDeref,
+    dotOperators,
+    callOperator,
+    parallel,
+    destructor,
+    notnil
 
-proc newConfigRef*(): ConfigRef =
-  result = ConfigRef(cppDefines: initSet[string](),
-    headerFile: "")
+  SymbolFilesOption* = enum
+    disabledSf, enabledSf, writeOnlySf, readOnlySf, v2Sf
 
-proc cppDefine*(c: ConfigRef; define: string) =
-  c.cppDefines.incl define
+  ConfigRef* = ref object ## eventually all global configuration should be moved here
+    linesCompiled*: int  # all lines that have been compiled
+    options*: TOptions
+    globalOptions*: TGlobalOptions
+    exitcode*: int8
+    cmd*: TCommands  # the command
+    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
 
-var
-  gIdeCmd*: IdeCmd
-  gOldNewlines*: bool
+    cppDefines*: HashSet[string]
+    headerFile*: string
+    features*: set[Feature]
+    arguments*: string ## the arguments to be passed to the program that
+                       ## should be run
+    helpWritten*: bool
+    ideCmd*: IdeCmd
+    oldNewlines*: bool
+    enableNotes*: TNoteKinds
+    disableNotes*: TNoteKinds
+    foreignPackageNotes*: TNoteKinds
+    notes*: TNoteKinds
+    mainPackageNotes*: TNoteKinds
+    errorCounter*: int
+    hintCounter*: int
+    warnCounter*: int
+    errorMax*: int
+    configVars*: StringTableRef
+    symbols*: StringTableRef ## We need to use a StringTableRef here as defined
+                             ## symbols are always guaranteed to be style
+                             ## insensitive. Otherwise hell would break lose.
+    packageCache*: StringTableRef
+    searchPaths*: seq[string]
+    lazyPaths*: seq[string]
+    outFile*, prefixDir*, libpath*, nimcacheDir*: string
+    dllOverrides, moduleOverrides*: StringTableRef
+    projectName*: string # holds a name like 'nim'
+    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
+    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
+    implicitImports*: seq[string] # modules that are to be implicitly imported
+    implicitIncludes*: seq[string] # modules that are to be implicitly included
+    docSeeSrcUrl*: string # if empty, no seeSrc will be generated. \
+    # The string uses the formatting variables `path` and `line`.
+
+const oldExperimentalFeatures* = {implicitDeref, dotOperators, callOperator, parallel}
 
 const
   ChecksOptions* = {optObjCheck, optFieldCheck, optRangeCheck, optNilCheck,
     optOverflowCheck, optBoundsCheck, optAssert, optNaNCheck, optInfCheck,
     optMoveCheck}
 
-var
-  gOptions*: TOptions = {optObjCheck, optFieldCheck, optRangeCheck,
-                         optBoundsCheck, optOverflowCheck, optAssert, optWarns,
-                         optHints, optStackTrace, optLineTrace,
-                         optPatterns, optNilCheck, optMoveCheck}
-  gGlobalOptions*: TGlobalOptions = {optThreadAnalysis}
-  gExitcode*: int8
-  gCmd*: TCommands = cmdNone  # the command
-  gSelectedGC* = gcRefc       # the selected GC
-  searchPaths*: seq[string] = @[]
-  lazyPaths*: seq[string]   = @[]
-  outFile*: string = ""
-  docSeeSrcUrl*: string = ""  # if empty, no seeSrc will be generated. \
-  # The string uses the formatting variables `path` and `line`.
-  #headerFile*: string = ""
-  gVerbosity* = 1             # how verbose the compiler is
-  gNumberOfProcessors*: int   # number of processors
-  gWholeProject*: bool        # for 'doc2': output any dependency
-  gEvalExpr* = ""             # expression for idetools --eval
-  gLastCmdTime*: float        # when caas is enabled, we measure each command
-  gListFullPaths*: bool
-  gPreciseStack*: bool = false
-  gNoNimblePath* = false
-  gExperimentalMode*: bool
-  newDestructors*: bool
-  gDynlibOverrideAll*: bool
-  useNimNamespace*: bool
+  DefaultOptions* = {optObjCheck, optFieldCheck, optRangeCheck,
+    optBoundsCheck, optOverflowCheck, optAssert, optWarns,
+    optHints, optStackTrace, optLineTrace,
+    optPatterns, optNilCheck, optMoveCheck}
+  DefaultGlobalOptions* = {optThreadAnalysis}
 
-type
-  SymbolFilesOption* = enum
-    disabledSf, enabledSf, writeOnlySf, readOnlySf, v2Sf
+template newPackageCache*(): untyped =
+  newStringTable(when FileSystemCaseSensitive:
+                   modeCaseInsensitive
+                 else:
+                   modeCaseSensitive)
 
-var gSymbolFiles*: SymbolFilesOption
+proc newConfigRef*(): ConfigRef =
+  result = ConfigRef(
+    selectedGC: gcRefc,
+    verbosity: 1,
+    options: DefaultOptions,
+    globalOptions: DefaultGlobalOptions,
+    evalExpr: "",
+    cppDefines: initSet[string](),
+    headerFile: "", features: {}, foreignPackageNotes: {hintProcessing, warnUnknownMagic,
+    hintQuitCalled, hintExecuting},
+    notes: NotesVerbosity[1], mainPackageNotes: NotesVerbosity[1],
+    configVars: newStringTable(modeStyleInsensitive),
+    symbols: newStringTable(modeStyleInsensitive),
+    packageCache: newPackageCache(),
+    searchPaths: @[],
+    lazyPaths: @[],
+    outFile: "", prefixDir: "", libpath: "", nimcacheDir: "",
+    dllOverrides: newStringTable(modeCaseInsensitive),
+    moduleOverrides: newStringTable(modeStyleInsensitive),
+    projectName: "", # holds a name like 'nim'
+    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
+    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: ""
+  )
+  # enable colors by default on terminals
+  if terminal.isatty(stderr):
+    incl(result.globalOptions, optUseColors)
+
+proc newPartialConfigRef*(): ConfigRef =
+  ## create a new ConfigRef that is only good enough for error reporting.
+  result = ConfigRef(
+    selectedGC: gcRefc,
+    verbosity: 1,
+    options: DefaultOptions,
+    globalOptions: DefaultGlobalOptions,
+    foreignPackageNotes: {hintProcessing, warnUnknownMagic,
+    hintQuitCalled, hintExecuting},
+    notes: NotesVerbosity[1], mainPackageNotes: NotesVerbosity[1])
 
-proc importantComments*(): bool {.inline.} = gCmd in {cmdDoc, cmdIdeTools}
-proc usesNativeGC*(): bool {.inline.} = gSelectedGC >= gcRefc
-template preciseStack*(): bool = gPreciseStack
+proc cppDefine*(c: ConfigRef; define: string) =
+  c.cppDefines.incl define
 
-template compilationCachePresent*: untyped =
-  gSymbolFiles in {enabledSf, writeOnlySf}
+proc isDefined*(conf: ConfigRef; symbol: string): bool =
+  if conf.symbols.hasKey(symbol):
+    result = conf.symbols[symbol] != "false"
+  elif cmpIgnoreStyle(symbol, CPU[targetCPU].name) == 0:
+    result = true
+  elif cmpIgnoreStyle(symbol, platform.OS[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 "posix", "unix":
+      result = 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}
+    of "bsd":
+      result = 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
+    of "nimrawsetjmp":
+      result = targetOS in {osSolaris, osNetbsd, osFreebsd, osOpenbsd,
+                            osDragonfly, osMacosx}
+    else: discard
+
+proc importantComments*(conf: ConfigRef): bool {.inline.} = conf.cmd in {cmdDoc, cmdIdeTools}
+proc usesNativeGC*(conf: ConfigRef): bool {.inline.} = conf.selectedGC >= gcRefc
+
+template compilationCachePresent*(conf: ConfigRef): untyped =
+  conf.symbolFiles in {enabledSf, writeOnlySf}
 #  {optCaasEnabled, optSymbolFiles} * gGlobalOptions != {}
 
-template optPreserveOrigSource*: untyped =
-  optEmbedOrigSrc in gGlobalOptions
+template optPreserveOrigSource*(conf: ConfigRef): untyped =
+  optEmbedOrigSrc in conf.globalOptions
 
 const
   genSubDir* = "nimcache"
@@ -181,82 +303,62 @@ const
   DocConfig* = "nimdoc.cfg"
   DocTexConfig* = "nimdoc.tex.cfg"
 
-# additional configuration variables:
-var
-  gConfigVars* = newStringTable(modeStyleInsensitive)
-  gDllOverrides = newStringTable(modeCaseInsensitive)
-  gModuleOverrides* = newStringTable(modeStyleInsensitive)
-  gPrefixDir* = "" # Overrides the default prefix dir in getPrefixDir proc.
-  libpath* = ""
-  gProjectName* = "" # holds a name like 'nim'
-  gProjectPath* = "" # holds a path like /home/alice/projects/nim/compiler/
-  gProjectFull* = "" # projectPath/projectName
-  gProjectIsStdin* = false # whether we're compiling from stdin
-  gProjectMainIdx*: int32 # the canonical path id of the main module
-  nimcacheDir* = ""
-  command* = "" # the main command (e.g. cc, check, scan, etc)
-  commandArgs*: seq[string] = @[] # any arguments after the main command
-  gKeepComments*: bool = true # whether the parser needs to keep comments
-  implicitImports*: seq[string] = @[] # modules that are to be implicitly imported
-  implicitIncludes*: seq[string] = @[] # modules that are to be implicitly included
-
 const oKeepVariableNames* = true
 
-template compilingLib*: bool =
+template compilingLib*(conf: ConfigRef): bool =
   gGlobalOptions * {optGenGuiApp, optGenDynLib} != {}
 
-proc mainCommandArg*: string =
+proc mainCommandArg*(conf: ConfigRef): string =
   ## This is intended for commands like check or parse
   ## which will work on the main project file unless
   ## explicitly given a specific file argument
-  if commandArgs.len > 0:
-    result = commandArgs[0]
+  if conf.commandArgs.len > 0:
+    result = conf.commandArgs[0]
   else:
-    result = gProjectName
+    result = conf.projectName
 
-proc existsConfigVar*(key: string): bool =
-  result = hasKey(gConfigVars, key)
+proc existsConfigVar*(conf: ConfigRef; key: string): bool =
+  result = hasKey(conf.configVars, key)
 
-proc getConfigVar*(key: string): string =
-  result = gConfigVars.getOrDefault key
+proc getConfigVar*(conf: ConfigRef; key: string): string =
+  result = conf.configVars.getOrDefault key
 
-proc setConfigVar*(key, val: string) =
-  gConfigVars[key] = val
+proc setConfigVar*(conf: ConfigRef; key, val: string) =
+  conf.configVars[key] = val
 
-proc getOutFile*(filename, ext: string): string =
-  if options.outFile != "": result = options.outFile
+proc getOutFile*(conf: ConfigRef; filename, ext: string): string =
+  if conf.outFile != "": result = conf.outFile
   else: result = changeFileExt(filename, ext)
 
-proc getPrefixDir*(): string =
+proc getPrefixDir*(conf: ConfigRef): string =
   ## Gets the prefix dir, usually the parent directory where the binary resides.
   ##
-  ## This is overridden by some tools (namely nimsuggest) via the ``gPrefixDir``
+  ## This is overridden by some tools (namely nimsuggest) via the ``conf.prefixDir``
   ## global.
-  if gPrefixDir != "": result = gPrefixDir
-  else:
-    result = splitPath(getAppDir()).head
+  if conf.prefixDir != "": result = conf.prefixDir
+  else: result = splitPath(getAppDir()).head
 
-proc setDefaultLibpath*() =
+proc setDefaultLibpath*(conf: ConfigRef) =
   # set default value (can be overwritten):
-  if libpath == "":
+  if conf.libpath == "":
     # choose default libpath:
-    var prefix = getPrefixDir()
+    var prefix = getPrefixDir(conf)
     when defined(posix):
-      if prefix == "/usr": libpath = "/usr/lib/nim"
-      elif prefix == "/usr/local": libpath = "/usr/local/lib/nim"
-      else: libpath = joinPath(prefix, "lib")
-    else: libpath = joinPath(prefix, "lib")
+      if prefix == "/usr": conf.libpath = "/usr/lib/nim"
+      elif prefix == "/usr/local": conf.libpath = "/usr/local/lib/nim"
+      else: conf.libpath = joinPath(prefix, "lib")
+    else: conf.libpath = joinPath(prefix, "lib")
 
     # Special rule to support other tools (nimble) which import the compiler
     # modules and make use of them.
     let realNimPath = findExe("nim")
     # Find out if $nim/../../lib/system.nim exists.
-    let parentNimLibPath = realNimPath.parentDir().parentDir() / "lib"
-    if not fileExists(libpath / "system.nim") and
+    let parentNimLibPath = realNimPath.parentDir.parentDir / "lib"
+    if not fileExists(conf.libpath / "system.nim") and
         fileExists(parentNimlibPath / "system.nim"):
-      libpath = parentNimLibPath
+      conf.libpath = parentNimLibPath
 
-proc canonicalizePath*(path: string): string =
+proc canonicalizePath*(conf: ConfigRef; path: string): string =
   # on Windows, 'expandFilename' calls getFullPathName which doesn't do
   # case corrections, so we have to use this convoluted way of retrieving
   # the true filename (see tests/modules and Nimble uses 'import Uri' instead
@@ -268,12 +370,12 @@ proc canonicalizePath*(path: string): string =
   else:
     result = path.expandFilename
 
-proc shortenDir*(dir: string): string =
+proc shortenDir*(conf: ConfigRef; dir: string): string =
   ## returns the interesting part of a dir
-  var prefix = gProjectPath & DirSep
+  var prefix = conf.projectPath & DirSep
   if startsWith(dir, prefix):
     return substr(dir, len(prefix))
-  prefix = getPrefixDir() & DirSep
+  prefix = getPrefixDir(conf) & DirSep
   if startsWith(dir, prefix):
     return substr(dir, len(prefix))
   result = dir
@@ -284,114 +386,89 @@ proc removeTrailingDirSep*(path: string): string =
   else:
     result = path
 
-proc disableNimblePath*() =
-  gNoNimblePath = true
-  lazyPaths.setLen(0)
+proc disableNimblePath*(conf: ConfigRef) =
+  incl conf.globalOptions, optNoNimblePath
+  conf.lazyPaths.setLen(0)
 
 include packagehandling
 
-proc getNimcacheDir*: string =
-  result = if nimcacheDir.len > 0: nimcacheDir else: gProjectPath.shortenDir /
-                                                         genSubDir
+proc getNimcacheDir*(conf: ConfigRef): string =
+  result = if conf.nimcacheDir.len > 0: conf.nimcacheDir
+           else: shortenDir(conf, conf.projectPath) / genSubDir
 
-
-proc pathSubs*(p, config: string): string =
+proc pathSubs*(conf: ConfigRef; p, config: string): string =
   let home = removeTrailingDirSep(os.getHomeDir())
   result = unixToNativePath(p % [
-    "nim", getPrefixDir(),
-    "lib", libpath,
+    "nim", getPrefixDir(conf),
+    "lib", conf.libpath,
     "home", home,
     "config", config,
-    "projectname", options.gProjectName,
-    "projectpath", options.gProjectPath,
-    "projectdir", options.gProjectPath,
-    "nimcache", getNimcacheDir()])
+    "projectname", conf.projectName,
+    "projectpath", conf.projectPath,
+    "projectdir", conf.projectPath,
+    "nimcache", getNimcacheDir(conf)])
   if "~/" in result:
     result = result.replace("~/", home & '/')
 
-proc toGeneratedFile*(path, ext: string): string =
+proc toGeneratedFile*(conf: ConfigRef; path, ext: string): string =
   ## converts "/home/a/mymodule.nim", "rod" to "/home/a/nimcache/mymodule.rod"
   var (head, tail) = splitPath(path)
   #if len(head) > 0: head = shortenDir(head & dirSep)
-  result = joinPath([getNimcacheDir(), changeFileExt(tail, ext)])
+  result = joinPath([getNimcacheDir(conf), changeFileExt(tail, ext)])
   #echo "toGeneratedFile(", path, ", ", ext, ") = ", result
 
-when noTimeMachine:
-  var alreadyExcludedDirs = initSet[string]()
-  proc excludeDirFromTimeMachine(dir: string) {.raises: [].} =
-    ## Calls a macosx command on the directory to exclude it from backups.
-    ##
-    ## The macosx tmutil command is invoked to mark the specified path as an
-    ## item to be excluded from time machine backups. If a path already exists
-    ## with files before excluding it, newer files won't be added to the
-    ## directory, but previous files won't be removed from the backup until the
-    ## user deletes that directory.
-    ##
-    ## The whole proc is optional and will ignore all kinds of errors. The only
-    ## way to be sure that it works is to call ``tmutil isexcluded path``.
-    if alreadyExcludedDirs.contains(dir): return
-    alreadyExcludedDirs.incl(dir)
-    try:
-      var p = startProcess("/usr/bin/tmutil", args = ["addexclusion", dir])
-      discard p.waitForExit
-      p.close
-    except Exception:
-      discard
-
-proc completeGeneratedFilePath*(f: string, createSubDir: bool = true): string =
+proc completeGeneratedFilePath*(conf: ConfigRef; f: string, createSubDir: bool = true): string =
   var (head, tail) = splitPath(f)
   #if len(head) > 0: head = removeTrailingDirSep(shortenDir(head & dirSep))
-  var subdir = getNimcacheDir() # / head
+  var subdir = getNimcacheDir(conf) # / head
   if createSubDir:
     try:
       createDir(subdir)
-      when noTimeMachine:
-        excludeDirFromTimeMachine(subdir)
     except OSError:
       writeLine(stdout, "cannot create directory: " & subdir)
       quit(1)
   result = joinPath(subdir, tail)
   #echo "completeGeneratedFilePath(", f, ") = ", result
 
-proc rawFindFile(f: string): string =
-  for it in searchPaths:
+proc rawFindFile(conf: ConfigRef; f: string): string =
+  for it in conf.searchPaths:
     result = joinPath(it, f)
     if existsFile(result):
-      return result.canonicalizePath
+      return canonicalizePath(conf, result)
   result = ""
 
-proc rawFindFile2(f: string): string =
-  for i, it in lazyPaths:
+proc rawFindFile2(conf: ConfigRef; f: string): string =
+  for i, it in conf.lazyPaths:
     result = joinPath(it, f)
     if existsFile(result):
       # bring to front
       for j in countDown(i,1):
-        swap(lazyPaths[j], lazyPaths[j-1])
+        swap(conf.lazyPaths[j], conf.lazyPaths[j-1])
 
-      return result.canonicalizePath
+      return canonicalizePath(conf, result)
   result = ""
 
-template patchModule() {.dirty.} =
-  if result.len > 0 and gModuleOverrides.len > 0:
-    let key = getPackageName(result) & "_" & splitFile(result).name
-    if gModuleOverrides.hasKey(key):
-      let ov = gModuleOverrides[key]
+template patchModule(conf: ConfigRef) {.dirty.} =
+  if result.len > 0 and conf.moduleOverrides.len > 0:
+    let key = getPackageName(conf, result) & "_" & splitFile(result).name
+    if conf.moduleOverrides.hasKey(key):
+      let ov = conf.moduleOverrides[key]
       if ov.len > 0: result = ov
 
-proc findFile*(f: string): string {.procvar.} =
+proc findFile*(conf: ConfigRef; f: string): string {.procvar.} =
   if f.isAbsolute:
     result = if f.existsFile: f else: ""
   else:
-    result = f.rawFindFile
+    result = rawFindFile(conf, f)
     if result.len == 0:
-      result = f.toLowerAscii.rawFindFile
+      result = rawFindFile(conf, f.toLowerAscii)
       if result.len == 0:
-        result = f.rawFindFile2
+        result = rawFindFile2(conf, f)
         if result.len == 0:
-          result = f.toLowerAscii.rawFindFile2
-  patchModule()
+          result = rawFindFile2(conf, f.toLowerAscii)
+  patchModule(conf)
 
-proc findModule*(modulename, currentModule: string): string =
+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
@@ -401,16 +478,16 @@ proc findModule*(modulename, currentModule: string): string =
       let currentPath = currentModule.splitFile.dir
       result = currentPath / m
       if not existsFile(result):
-        result = findFile(m)
+        result = findFile(conf, m)
         if existsFile(result): return result
   let m = addFileExt(modulename, NimExt)
   let currentPath = currentModule.splitFile.dir
   result = currentPath / m
   if not existsFile(result):
-    result = findFile(m)
-  patchModule()
+    result = findFile(conf, m)
+  patchModule(conf)
 
-proc findProjectNimFile*(pkg: string): string =
+proc findProjectNimFile*(conf: ConfigRef; pkg: string): string =
   const extensions = [".nims", ".cfg", ".nimcfg", ".nimble"]
   var candidates: seq[string] = @[]
   for k, f in os.walkDir(pkg, relative=true):
@@ -435,11 +512,12 @@ proc canonDynlibName(s: string): string =
   else:
     result = s.substr(start)
 
-proc inclDynlibOverride*(lib: string) =
-  gDllOverrides[lib.canonDynlibName] = "true"
+proc inclDynlibOverride*(conf: ConfigRef; lib: string) =
+  conf.dllOverrides[lib.canonDynlibName] = "true"
 
-proc isDynlibOverride*(lib: string): bool =
-  result = gDynlibOverrideAll or gDllOverrides.hasKey(lib.canonDynlibName)
+proc isDynlibOverride*(conf: ConfigRef; lib: string): bool =
+  result = optDynlibOverrideAll in conf.globalOptions or
+     conf.dllOverrides.hasKey(lib.canonDynlibName)
 
 proc binaryStrSearch*(x: openArray[string], y: string): int =
   var a = 0
diff --git a/compiler/packagehandling.nim b/compiler/packagehandling.nim
index 758411e39..2efab58b0 100644
--- a/compiler/packagehandling.nim
+++ b/compiler/packagehandling.nim
@@ -15,23 +15,16 @@ iterator myParentDirs(p: string): string =
     if current.len == 0: break
     yield current
 
-template newPackageCache(): untyped =
-  newStringTable(when FileSystemCaseSensitive:
-                   modeCaseInsensitive
-                 else:
-                   modeCaseSensitive)
+proc resetPackageCache*(conf: ConfigRef) =
+  conf.packageCache = newPackageCache()
 
-var packageCache = newPackageCache()
-
-proc resetPackageCache*() = packageCache = newPackageCache()
-
-proc getPackageName*(path: string): string =
+proc getPackageName*(conf: ConfigRef; path: string): string =
   var parents = 0
   block packageSearch:
     for d in myParentDirs(path):
-      if packageCache.hasKey(d):
+      if conf.packageCache.hasKey(d):
         #echo "from cache ", d, " |", packageCache[d], "|", path.splitFile.name
-        return packageCache[d]
+        return conf.packageCache[d]
       inc parents
       for file in walkFiles(d / "*.nimble"):
         result = file.splitFile.name
@@ -43,12 +36,12 @@ proc getPackageName*(path: string): string =
   if result.isNil: result = ""
   for d in myParentDirs(path):
     #echo "set cache ", d, " |", result, "|", parents
-    packageCache[d] = result
+    conf.packageCache[d] = result
     dec parents
     if parents <= 0: break
 
-proc withPackageName*(path: string): string =
-  let x = path.getPackageName
+proc withPackageName*(conf: ConfigRef; path: string): string =
+  let x = getPackageName(conf, path)
   if x.len == 0:
     result = path
   else:
diff --git a/compiler/parampatterns.nim b/compiler/parampatterns.nim
index 8540f1b32..944aec048 100644
--- a/compiler/parampatterns.nim
+++ b/compiler/parampatterns.nim
@@ -10,7 +10,8 @@
 ## This module implements the pattern matching features for term rewriting
 ## macro support.
 
-import strutils, ast, astalgo, types, msgs, idents, renderer, wordrecg, trees
+import strutils, ast, astalgo, types, msgs, idents, renderer, wordrecg, trees,
+  options
 
 # we precompile the pattern here for efficiency into some internal
 # stack based VM :-) Why? Because it's fun; I did no benchmarks to see if that
@@ -41,8 +42,8 @@ type
 const
   MaxStackSize* = 64 ## max required stack size by the VM
 
-proc patternError(n: PNode) =
-  localError(n.info, errIllFormedAstX, renderTree(n, {renderNoComments}))
+proc patternError(n: PNode; conf: ConfigRef) =
+  localError(conf, n.info, "illformed AST: " & renderTree(n, {renderNoComments}))
 
 proc add(code: var TPatternCode, op: TOpcode) {.inline.} =
   add(code, chr(ord(op)))
@@ -53,42 +54,42 @@ proc whichAlias*(p: PSym): TAliasRequest =
   else:
     result = aqNone
 
-proc compileConstraints(p: PNode, result: var TPatternCode) =
+proc compileConstraints(p: PNode, result: var TPatternCode; conf: ConfigRef) =
   case p.kind
   of nkCallKinds:
     if p.sons[0].kind != nkIdent:
-      patternError(p.sons[0])
+      patternError(p.sons[0], conf)
       return
     let op = p.sons[0].ident
     if p.len == 3:
       if op.s == "|" or op.id == ord(wOr):
-        compileConstraints(p.sons[1], result)
-        compileConstraints(p.sons[2], result)
+        compileConstraints(p.sons[1], result, conf)
+        compileConstraints(p.sons[2], result, conf)
         result.add(ppOr)
       elif op.s == "&" or op.id == ord(wAnd):
-        compileConstraints(p.sons[1], result)
-        compileConstraints(p.sons[2], result)
+        compileConstraints(p.sons[1], result, conf)
+        compileConstraints(p.sons[2], result, conf)
         result.add(ppAnd)
       else:
-        patternError(p)
+        patternError(p, conf)
     elif p.len == 2 and (op.s == "~" or op.id == ord(wNot)):
-      compileConstraints(p.sons[1], result)
+      compileConstraints(p.sons[1], result, conf)
       result.add(ppNot)
     else:
-      patternError(p)
+      patternError(p, conf)
   of nkAccQuoted, nkPar:
     if p.len == 1:
-      compileConstraints(p.sons[0], result)
+      compileConstraints(p.sons[0], result, conf)
     else:
-      patternError(p)
+      patternError(p, conf)
   of nkIdent:
     let spec = p.ident.s.normalize
     case spec
-    of "atom":  result.add(ppAtom)
-    of "lit":   result.add(ppLit)
-    of "sym":   result.add(ppSym)
+    of "atom": result.add(ppAtom)
+    of "lit": result.add(ppLit)
+    of "sym": result.add(ppSym)
     of "ident": result.add(ppIdent)
-    of "call":  result.add(ppCall)
+    of "call": result.add(ppCall)
     of "alias": result[0] = chr(aqShouldAlias.ord)
     of "noalias": result[0] = chr(aqNoAlias.ord)
     of "lvalue": result.add(ppLValue)
@@ -97,24 +98,24 @@ proc compileConstraints(p: PNode, result: var TPatternCode) =
     of "nosideeffect": result.add(ppNoSideEffect)
     else:
       # check all symkinds:
-      internalAssert int(high(TSymKind)) < 255
+      internalAssert conf, int(high(TSymKind)) < 255
       for i in low(TSymKind)..high(TSymKind):
         if cmpIgnoreStyle(($i).substr(2), spec) == 0:
           result.add(ppSymKind)
           result.add(chr(i.ord))
           return
       # check all nodekinds:
-      internalAssert int(high(TNodeKind)) < 255
+      internalAssert conf, int(high(TNodeKind)) < 255
       for i in low(TNodeKind)..high(TNodeKind):
         if cmpIgnoreStyle($i, spec) == 0:
           result.add(ppNodeKind)
           result.add(chr(i.ord))
           return
-      patternError(p)
+      patternError(p, conf)
   else:
-    patternError(p)
+    patternError(p, conf)
 
-proc semNodeKindConstraints*(p: PNode): PNode =
+proc semNodeKindConstraints*(p: PNode; conf: ConfigRef): PNode =
   ## does semantic checking for a node kind pattern and compiles it into an
   ## efficient internal format.
   assert p.kind == nkCurlyExpr
@@ -123,11 +124,11 @@ proc semNodeKindConstraints*(p: PNode): PNode =
   result.strVal.add(chr(aqNone.ord))
   if p.len >= 2:
     for i in 1..<p.len:
-      compileConstraints(p.sons[i], result.strVal)
+      compileConstraints(p.sons[i], result.strVal, conf)
     if result.strVal.len > MaxStackSize-1:
-      internalError(p.info, "parameter pattern too complex")
+      internalError(conf, p.info, "parameter pattern too complex")
   else:
-    patternError(p)
+    patternError(p, conf)
   result.strVal.add(ppEof)
 
 type
@@ -235,7 +236,8 @@ proc isAssignable*(owner: PSym, n: PNode; isUnsafeAddr=false): TAssignableResult
       result = arLValue
     else:
       result = isAssignable(owner, n.sons[0], isUnsafeAddr)
-    if result != arNone and sfDiscriminant in n.sons[1].sym.flags:
+    if result != arNone and n[1].kind == nkSym and
+        sfDiscriminant in n[1].sym.flags:
       result = arDiscriminant
   of nkBracketExpr:
     if skipTypes(n.sons[0].typ, abstractInst-{tyTypeDesc}).kind in
diff --git a/compiler/parser.nim b/compiler/parser.nim
index 0019b7acb..fbc57ebb6 100644
--- a/compiler/parser.nim
+++ b/compiler/parser.nim
@@ -27,7 +27,7 @@ when isMainModule:
   outp.close
 
 import
-  llstream, lexer, idents, strutils, ast, astalgo, msgs
+  llstream, lexer, idents, strutils, ast, astalgo, msgs, options, configuration
 
 type
   TParser* = object            # A TParser object represents a file that
@@ -83,21 +83,21 @@ proc getTok(p: var TParser) =
   rawGetTok(p.lex, p.tok)
   p.hasProgress = true
 
-proc openParser*(p: var TParser, fileIdx: int32, inputStream: PLLStream,
-                 cache: IdentCache;
+proc openParser*(p: var TParser, fileIdx: FileIndex, inputStream: PLLStream,
+                 cache: IdentCache; config: ConfigRef;
                  strongSpaces=false) =
   ## Open a parser, using the given arguments to set up its internal state.
   ##
   initToken(p.tok)
-  openLexer(p.lex, fileIdx, inputStream, cache)
+  openLexer(p.lex, fileIdx, inputStream, cache, config)
   getTok(p)                   # read the first token
   p.firstTok = true
   p.strongSpaces = strongSpaces
 
 proc openParser*(p: var TParser, filename: string, inputStream: PLLStream,
-                 cache: IdentCache;
+                 cache: IdentCache; config: ConfigRef;
                  strongSpaces=false) =
-  openParser(p, filename.fileInfoIdx, inputStream, cache, strongSpaces)
+  openParser(p, fileInfoIdx(config, filename), inputStream, cache, config, strongSpaces)
 
 proc closeParser(p: var TParser) =
   ## Close a parser, freeing up its resources.
@@ -107,9 +107,13 @@ proc parMessage(p: TParser, msg: TMsgKind, arg = "") =
   ## Produce and emit the parser message `arg` to output.
   lexMessageTok(p.lex, msg, p.tok, arg)
 
-proc parMessage(p: TParser, msg: TMsgKind, tok: TToken) =
+proc parMessage(p: TParser, msg: string, tok: TToken) =
   ## Produce and emit a parser message to output about the token `tok`
-  parMessage(p, msg, prettyTok(tok))
+  parMessage(p, errGenerated, msg % prettyTok(tok))
+
+proc parMessage(p: TParser, arg: string) =
+  ## Produce and emit the parser message `arg` to output.
+  lexMessageTok(p.lex, errGenerated, p.tok, arg)
 
 template withInd(p, body: untyped) =
   let oldInd = p.currInd
@@ -125,7 +129,13 @@ proc rawSkipComment(p: var TParser, node: PNode) =
   if p.tok.tokType == tkComment:
     if node != nil:
       if node.comment == nil: node.comment = ""
-      add(node.comment, p.tok.literal)
+      when defined(nimpretty):
+        if p.tok.commentOffsetB > p.tok.commentOffsetA:
+          add node.comment, fileSection(p.lex.fileIdx, p.tok.commentOffsetA, p.tok.commentOffsetB)
+        else:
+          add node.comment, p.tok.literal
+      else:
+        add(node.comment, p.tok.literal)
     else:
       parMessage(p, errInternal, "skipComment")
     getTok(p)
@@ -136,6 +146,12 @@ proc skipComment(p: var TParser, node: PNode) =
 proc flexComment(p: var TParser, node: PNode) =
   if p.tok.indent < 0 or realInd(p): rawSkipComment(p, node)
 
+const
+  errInvalidIndentation = "invalid indentation"
+  errIdentifierExpected = "identifier expected, but got '$1'"
+  errExprExpected = "expression expected, but found '$1'"
+  errTokenExpected = "'$1' expected"
+
 proc skipInd(p: var TParser) =
   if p.tok.indent >= 0:
     if not realInd(p): parMessage(p, errInvalidIndentation)
@@ -154,11 +170,11 @@ proc getTokNoInd(p: var TParser) =
 
 proc expectIdentOrKeyw(p: TParser) =
   if p.tok.tokType != tkSymbol and not isKeyword(p.tok.tokType):
-    lexMessage(p.lex, errIdentifierExpected, prettyTok(p.tok))
+    lexMessage(p.lex, errGenerated, errIdentifierExpected % prettyTok(p.tok))
 
 proc expectIdent(p: TParser) =
   if p.tok.tokType != tkSymbol:
-    lexMessage(p.lex, errIdentifierExpected, prettyTok(p.tok))
+    lexMessage(p.lex, errGenerated, errIdentifierExpected % prettyTok(p.tok))
 
 proc eat(p: var TParser, tokType: TTokType) =
   ## Move the parser to the next token if the current token is of type
@@ -166,7 +182,8 @@ proc eat(p: var TParser, tokType: TTokType) =
   if p.tok.tokType == tokType:
     getTok(p)
   else:
-    lexMessageTok(p.lex, errTokenExpected, p.tok, TokTypeToStr[tokType])
+    lexMessage(p.lex, errGenerated,
+      "expected: '" & TokTypeToStr[tokType] & "', but got: '" & prettyTok(p.tok) & "'")
 
 proc parLineInfo(p: TParser): TLineInfo =
   ## Retrieve the line information associated with the parser's current state.
@@ -262,13 +279,9 @@ proc isUnary(p: TParser): bool =
 proc checkBinary(p: TParser) {.inline.} =
   ## Check if the current parser token is a binary operator.
   # we don't check '..' here as that's too annoying
-  if p.strongSpaces and p.tok.tokType == tkOpr:
+  if p.tok.tokType == tkOpr:
     if p.tok.strongSpaceB > 0 and p.tok.strongSpaceA != p.tok.strongSpaceB:
-      parMessage(p, errGenerated,
-                 "Number of spaces around '$#' not consistent" %
-                 prettyTok(p.tok))
-    elif p.tok.strongSpaceA notin {0,1,2,4,8}:
-      parMessage(p, errGenerated, "Number of spaces must be 0,1,2,4 or 8")
+      parMessage(p, warnInconsistentSpacing, prettyTok(p.tok))
 
 #| module = stmt ^* (';' / IND{=})
 #|
@@ -700,10 +713,17 @@ proc namedParams(p: var TParser, callee: PNode,
   # progress guaranteed
   exprColonEqExprListAux(p, endTok, result)
 
-proc commandParam(p: var TParser): PNode =
+proc commandParam(p: var TParser, isFirstParam: var bool): PNode =
   result = parseExpr(p)
   if p.tok.tokType == tkDo:
     result = postExprBlocks(p, result)
+  elif p.tok.tokType == tkEquals and not isFirstParam:
+    let lhs = result
+    result = newNodeP(nkExprEqExpr, p)
+    getTok(p)
+    addSon(result, lhs)
+    addSon(result, parseExpr(p))
+  isFirstParam = false
 
 proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode =
   #| primarySuffix = '(' (exprColonEqExpr comma?)* ')' doBlocks?
@@ -738,17 +758,19 @@ 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:
-      if p.inPragma == 0:
+    of tkSymbol, tkAccent, tkIntLit..tkCharLit, tkNil, tkCast, tkAddr, tkType,
+       tkOpr, tkDotDot:
+      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
         let a = result
         result = newNodeP(nkCommand, p)
         addSon(result, a)
+        var isFirstParam = true
         when true:
           # progress NOT guaranteed
           p.hasProgress = false
-          addSon result, commandParam(p)
+          addSon result, commandParam(p, isFirstParam)
           if not p.hasProgress: break
         else:
           while p.tok.tokType != tkEof:
@@ -875,7 +897,7 @@ proc parsePragma(p: var TParser): PNode =
       skipComment(p, a)
   optPar(p)
   if p.tok.tokType in {tkCurlyDotRi, tkCurlyRi}: getTok(p)
-  else: parMessage(p, errTokenExpected, ".}")
+  else: parMessage(p, "expected '.}'")
   dec p.inPragma
 
 proc identVis(p: var TParser; allowDot=false): PNode =
@@ -936,7 +958,7 @@ proc parseIdentColonEquals(p: var TParser, flags: TDeclaredIdentFlags): PNode =
   else:
     addSon(result, newNodeP(nkEmpty, p))
     if p.tok.tokType != tkEquals and withBothOptional notin flags:
-      parMessage(p, errColonOrEqualsExpected, p.tok)
+      parMessage(p, "':' or '=' expected, but got '$1'", p.tok)
   if p.tok.tokType == tkEquals:
     getTok(p)
     optInd(p, result)
@@ -1009,7 +1031,7 @@ proc parseParamList(p: var TParser, retColon = true): PNode =
         parMessage(p, errGenerated, "the syntax is 'parameter: var T', not 'var parameter: T'")
         break
       else:
-        parMessage(p, errTokenExpected, ")")
+        parMessage(p, "expected closing ')'")
         break
       addSon(result, a)
       if p.tok.tokType notin {tkComma, tkSemiColon}: break
@@ -1170,7 +1192,7 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode =
     if mode == pmTypeDef:
       result = parseTypeClass(p)
     else:
-      parMessage(p, errInvalidToken, p.tok)
+      parMessage(p, "the 'concept' keyword is only valid in 'type' sections")
   of tkStatic:
     let info = parLineInfo(p)
     getTokNoInd(p)
@@ -1280,7 +1302,7 @@ proc postExprBlocks(p: var TParser, x: PNode): PNode =
       if nextBlock.kind == nkElse: break
   else:
     if openingParams.kind != nkEmpty:
-      parMessage(p, errTokenExpected, ":")
+      parMessage(p, "expected ':'")
 
 proc parseExprStmt(p: var TParser): PNode =
   #| exprStmt = simpleExpr
@@ -1300,17 +1322,18 @@ proc parseExprStmt(p: var TParser): PNode =
     addSon(result, b)
   else:
     # simpleExpr parsed 'p a' from 'p a, b'?
+    var isFirstParam = false
     if p.tok.indent < 0 and p.tok.tokType == tkComma and a.kind == nkCommand:
       result = a
       while true:
         getTok(p)
         optInd(p, result)
-        addSon(result, commandParam(p))
+        addSon(result, commandParam(p, isFirstParam))
         if p.tok.tokType != tkComma: break
     elif p.tok.indent < 0 and isExprStart(p):
       result = newNode(nkCommand, a.info, @[a])
       while true:
-        addSon(result, commandParam(p))
+        addSon(result, commandParam(p, isFirstParam))
         if p.tok.tokType != tkComma: break
         getTok(p)
         optInd(p, result)
@@ -1515,7 +1538,7 @@ proc parseTry(p: var TParser; isExpr: bool): PNode =
     addSon(b, parseStmt(p))
     addSon(result, b)
     if b.kind == nkFinally: break
-  if b == nil: parMessage(p, errTokenExpected, "except")
+  if b == nil: parMessage(p, "expected 'except'")
 
 proc parseExceptBlock(p: var TParser, kind: TNodeKind): PNode =
   #| exceptBlock = 'except' colcom stmt
@@ -1570,7 +1593,7 @@ proc parseAsm(p: var TParser): PNode =
   of tkTripleStrLit: addSon(result,
                             newStrNodeP(nkTripleStrLit, p.tok.literal, p))
   else:
-    parMessage(p, errStringLiteralExpected)
+    parMessage(p, "the 'asm' statement takes a string literal")
     addSon(result, ast.emptyNode)
     return
   getTok(p)
@@ -1749,7 +1772,7 @@ proc parseEnum(p: var TParser): PNode =
         p.tok.tokType == tkEof:
       break
   if result.len <= 1:
-    lexMessageTok(p.lex, errIdentifierExpected, p.tok, prettyTok(p.tok))
+    parMessage(p, errIdentifierExpected, p.tok)
 
 proc parseObjectPart(p: var TParser): PNode
 proc parseObjectWhen(p: var TParser): PNode =
@@ -2112,7 +2135,7 @@ proc parseStmt(p: var TParser): PNode =
     case p.tok.tokType
     of tkIf, tkWhile, tkCase, tkTry, tkFor, tkBlock, tkAsm, tkProc, tkFunc,
        tkIterator, tkMacro, tkType, tkConst, tkWhen, tkVar:
-      parMessage(p, errComplexStmtRequiresInd)
+      parMessage(p, "complex statement requires indentation")
       result = ast.emptyNode
     else:
       if p.inSemiStmtList > 0:
@@ -2175,8 +2198,8 @@ proc parseTopLevelStmt(p: var TParser): PNode =
       if result.kind == nkEmpty: parMessage(p, errExprExpected, p.tok)
       break
 
-proc parseString*(s: string; cache: IdentCache; filename: string = "";
-                  line: int = 0;
+proc parseString*(s: string; cache: IdentCache; config: ConfigRef;
+                  filename: string = ""; line: int = 0;
                   errorHandler: TErrorHandler = nil): PNode =
   ## Parses a string into an AST, returning the top node.
   ## `filename` and `line`, although optional, provide info so that the
@@ -2189,7 +2212,7 @@ proc parseString*(s: string; cache: IdentCache; filename: string = "";
   # XXX for now the builtin 'parseStmt/Expr' functions do not know about strong
   # spaces...
   parser.lex.errorHandler = errorHandler
-  openParser(parser, filename, stream, cache, false)
+  openParser(parser, filename, stream, cache, config, false)
 
   result = parser.parseAll
   closeParser(parser)
diff --git a/compiler/passaux.nim b/compiler/passaux.nim
index 2065d5893..568fb4c23 100644
--- a/compiler/passaux.nim
+++ b/compiler/passaux.nim
@@ -10,22 +10,26 @@
 ## implements some little helper passes
 
 import
-  strutils, ast, astalgo, passes, idents, msgs, options, idgen
+  strutils, ast, astalgo, passes, idents, msgs, options, idgen, configuration
 
 from modulegraphs import ModuleGraph
 
+type
+  VerboseRef = ref object of TPassContext
+    config: ConfigRef
+
 proc verboseOpen(graph: ModuleGraph; s: PSym; cache: IdentCache): PPassContext =
   #MessageOut('compiling ' + s.name.s);
-  result = nil                # we don't need a context
-  rawMessage(hintProcessing, s.name.s)
+  result = VerboseRef(config: graph.config)
+  rawMessage(graph.config, hintProcessing, s.name.s)
 
 proc verboseProcess(context: PPassContext, n: PNode): PNode =
   result = n
-  if context != nil: internalError("logpass: context is not nil")
-  if gVerbosity == 3:
+  let v = VerboseRef(context)
+  if v.config.verbosity == 3:
     # system.nim deactivates all hints, for verbosity:3 we want the processing
     # messages nonetheless, so we activate them again unconditionally:
-    incl(msgs.gNotes, hintProcessing)
-    message(n.info, hintProcessing, $idgen.gFrontendId)
+    incl(v.config.notes, hintProcessing)
+    message(v.config, n.info, hintProcessing, $idgen.gFrontendId)
 
 const verbosePass* = makePass(open = verboseOpen, process = verboseProcess)
diff --git a/compiler/passes.nim b/compiler/passes.nim
index f079100ea..8f9f57f3d 100644
--- a/compiler/passes.nim
+++ b/compiler/passes.nim
@@ -13,7 +13,8 @@
 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
+  nimsets, syntaxes, times, rodread, idgen, modulegraphs, reorder, rod,
+  configuration
 
 
 type
@@ -52,30 +53,16 @@ proc makePass*(open: TPassOpen = nil,
 
 # the semantic checker needs these:
 var
-  gImportModule*: proc (graph: ModuleGraph; m: PSym, fileIdx: int32; cache: IdentCache): PSym {.nimcall.}
-  gIncludeFile*: proc (graph: ModuleGraph; m: PSym, fileIdx: int32; cache: IdentCache): PNode {.nimcall.}
+  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*(n: PNode): bool {.inline.} =
+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
   # error count instead.
-  result = msgs.gErrorCounter > 0
-
-proc astNeeded*(s: PSym): bool =
-  # The ``rodwrite`` module uses this to determine if the body of a proc
-  # needs to be stored. The passes manager frees s.sons[codePos] when
-  # appropriate to free the procedure body's memory. This is important
-  # to keep memory usage down.
-  if (s.kind in {skMethod, skProc, skFunc}) and
-      ({sfCompilerProc, sfCompileTime} * s.flags == {}) and
-      (s.typ.callConv != ccInline) and
-      (s.ast.sons[genericParamsPos].kind == nkEmpty):
-    result = false
-    # XXX this doesn't really make sense with excessive CTFE
-  else:
-    result = true
+  result = config.errorCounter > 0
 
 const
   maxPasses = 10
@@ -153,20 +140,21 @@ proc closePassesCached(graph: ModuleGraph; a: var TPassContextArray) =
       m = gPasses[i].close(graph, a[i], m)
     a[i] = nil                # free the memory here
 
-proc resolveMod(module, relativeTo: string): int32 =
-  let fullPath = findModule(module, relativeTo)
+proc resolveMod(conf: ConfigRef; module, relativeTo: string): FileIndex =
+  let fullPath = findModule(conf, module, relativeTo)
   if fullPath.len == 0:
     result = InvalidFileIDX
   else:
-    result = fullPath.fileInfoIdx
+    result = fileInfoIdx(conf, fullPath)
 
-proc processImplicits(implicits: seq[string], nodeKind: TNodeKind,
+proc processImplicits(conf: ConfigRef; implicits: seq[string], nodeKind: TNodeKind,
                       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
   for module in items(implicits):
     # implicit imports should not lead to a module importing itself
-    if m.position != resolveMod(module, relativeTo):
+    if m.position != resolveMod(conf, module, relativeTo).int32:
       var importStmt = newNodeI(nodeKind, gCmdLineInfo)
       var str = newStrNode(nkStrLit, module)
       str.info = gCmdLineInfo
@@ -216,20 +204,20 @@ proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream,
       let filename = fileIdx.toFullPathConsiderDirty
       s = llStreamOpen(filename, fmRead)
       if s == nil:
-        rawMessage(errCannotOpenFile, filename)
+        rawMessage(graph.config, errCannotOpenFile, filename)
         return false
     else:
       s = stream
     while true:
-      openParsers(p, fileIdx, s, cache)
+      openParsers(p, fileIdx, s, cache, graph.config)
 
       if sfSystemModule notin module.flags:
         # XXX what about caching? no processing then? what if I change the
         # modules to include between compilation runs? we'd need to track that
         # in ROD files. I think we should enable this feature only
         # for the interactive mode.
-        processImplicits implicitImports, nkImportStmt, a, module
-        processImplicits implicitIncludes, nkIncludeStmt, a, module
+        processImplicits graph.config, graph.config.implicitImports, nkImportStmt, a, module
+        processImplicits graph.config, graph.config.implicitIncludes, nkIncludeStmt, a, module
 
       while true:
         if graph.stopCompile(): break
diff --git a/compiler/patterns.nim b/compiler/patterns.nim
index 31b76743e..5409a4811 100644
--- a/compiler/patterns.nim
+++ b/compiler/patterns.nim
@@ -150,7 +150,7 @@ proc matches(c: PPatternContext, p, n: PNode): bool =
     of "*": result = matchNested(c, p, n, rpn=false)
     of "**": result = matchNested(c, p, n, rpn=true)
     of "~": result = not matches(c, p.sons[1], n)
-    else: internalError(p.info, "invalid pattern")
+    else: doAssert(false, "invalid pattern")
     # template {add(a, `&` * b)}(a: string{noalias}, b: varargs[string]) =
     #   add(a, b)
   elif p.kind == nkCurlyExpr:
@@ -289,7 +289,7 @@ proc applyRule*(c: PContext, s: PSym, n: PNode): PNode =
         # constraint not fulfilled:
         if not ok: return nil
 
-  markUsed(n.info, s, c.graph.usageSym)
+  markUsed(c.config, n.info, s, c.graph.usageSym)
   if ctx.subMatch:
     assert m.len == 3
     m.sons[1] = result
diff --git a/compiler/pbraces.nim b/compiler/pbraces.nim
deleted file mode 100644
index eba6f0b62..000000000
--- a/compiler/pbraces.nim
+++ /dev/null
@@ -1,1790 +0,0 @@
-#
-#
-#           The Nim Compiler
-#        (c) Copyright 2017 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-# This module implements the parser of the braces Nim syntax.
-
-import
-  llstream, lexer, idents, strutils, ast, astalgo, msgs
-
-from parser import TParser
-
-proc getTok(p: var TParser) =
-  ## Get the next token from the parser's lexer, and store it in the parser's
-  ## `tok` member.
-  rawGetTok(p.lex, p.tok)
-
-proc openParser*(p: var TParser, fileIdx: int32, inputStream: PLLStream;
-                 cache: IdentCache) =
-  ## Open a parser, using the given arguments to set up its internal state.
-  ##
-  initToken(p.tok)
-  openLexer(p.lex, fileIdx, inputStream, cache)
-  getTok(p)                   # read the first token
-  p.lex.allowTabs = true
-
-proc openParser*(p: var TParser, filename: string, inputStream: PLLStream;
-                 cache: IdentCache) =
-  openParser(p, filename.fileInfoIdx, inputStream, cache)
-
-proc closeParser*(p: var TParser) =
-  ## Close a parser, freeing up its resources.
-  closeLexer(p.lex)
-
-proc parMessage(p: TParser, msg: TMsgKind, arg = "") =
-  ## Produce and emit the parser message `arg` to output.
-  lexMessageTok(p.lex, msg, p.tok, arg)
-
-proc parMessage(p: TParser, msg: TMsgKind, tok: TToken) =
-  ## Produce and emit a parser message to output about the token `tok`
-  parMessage(p, msg, prettyTok(tok))
-
-proc rawSkipComment(p: var TParser, node: PNode) =
-  if p.tok.tokType == tkComment:
-    if node != nil:
-      if node.comment == nil: node.comment = ""
-      add(node.comment, p.tok.literal)
-    else:
-      parMessage(p, errInternal, "skipComment")
-    getTok(p)
-
-proc skipComment(p: var TParser, node: PNode) =
-  rawSkipComment(p, node)
-
-proc flexComment(p: var TParser, node: PNode) =
-  rawSkipComment(p, node)
-
-proc skipInd(p: var TParser) = discard
-proc optPar(p: var TParser) = discard
-
-proc optInd(p: var TParser, n: PNode) =
-  skipComment(p, n)
-
-proc getTokNoInd(p: var TParser) =
-  getTok(p)
-
-proc expectIdentOrKeyw(p: TParser) =
-  if p.tok.tokType != tkSymbol and not isKeyword(p.tok.tokType):
-    lexMessage(p.lex, errIdentifierExpected, prettyTok(p.tok))
-
-proc expectIdent(p: TParser) =
-  if p.tok.tokType != tkSymbol:
-    lexMessage(p.lex, errIdentifierExpected, prettyTok(p.tok))
-
-proc eat(p: var TParser, tokType: TTokType) =
-  ## Move the parser to the next token if the current token is of type
-  ## `tokType`, otherwise error.
-  if p.tok.tokType == tokType:
-    getTok(p)
-  else:
-    lexMessageTok(p.lex, errTokenExpected, p.tok, TokTypeToStr[tokType])
-
-proc parLineInfo(p: TParser): TLineInfo =
-  ## Retrieve the line information associated with the parser's current state.
-  result = getLineInfo(p.lex, p.tok)
-
-proc indAndComment(p: var TParser, n: PNode) =
-  rawSkipComment(p, n)
-
-proc newNodeP(kind: TNodeKind, p: TParser): PNode =
-  result = newNodeI(kind, parLineInfo(p))
-
-proc newIntNodeP(kind: TNodeKind, intVal: BiggestInt, p: TParser): PNode =
-  result = newNodeP(kind, p)
-  result.intVal = intVal
-
-proc newFloatNodeP(kind: TNodeKind, floatVal: BiggestFloat,
-                   p: TParser): PNode =
-  result = newNodeP(kind, p)
-  result.floatVal = floatVal
-
-proc newStrNodeP(kind: TNodeKind, strVal: string, p: TParser): PNode =
-  result = newNodeP(kind, p)
-  result.strVal = strVal
-
-proc newIdentNodeP(ident: PIdent, p: TParser): PNode =
-  result = newNodeP(nkIdent, p)
-  result.ident = ident
-
-proc parseExpr(p: var TParser): PNode
-proc parseStmt(p: var TParser): PNode
-proc parseTypeDesc(p: var TParser): PNode
-proc parseDoBlocks(p: var TParser, call: PNode)
-proc parseParamList(p: var TParser, retColon = true): PNode
-proc parseStmtPragma(p: var TParser): PNode
-proc parseCase(p: var TParser): PNode
-proc parseTry(p: var TParser): PNode
-
-proc isSigilLike(tok: TToken): bool {.inline.} =
-  result = tok.tokType == tkOpr and tok.ident.s[0] == '@'
-
-proc isAt(tok: TToken): bool {.inline.} =
-  tok.tokType == tkOpr and tok.ident.s == "@" and tok.strongSpaceB == 0
-
-proc isRightAssociative(tok: TToken): bool {.inline.} =
-  ## Determines whether the token is right assocative.
-  result = tok.tokType == tkOpr and tok.ident.s[0] == '^'
-  # or (let L = tok.ident.s.len; L > 1 and tok.ident.s[L-1] == '>'))
-
-proc getPrecedence(tok: TToken): int =
-  ## Calculates the precedence of the given token.
-  template considerStrongSpaces(x): untyped = x
-
-  case tok.tokType
-  of tkOpr:
-    let L = tok.ident.s.len
-    let relevantChar = tok.ident.s[0]
-
-    # arrow like?
-    if L > 1 and tok.ident.s[L-1] == '>' and
-      tok.ident.s[L-2] in {'-', '~', '='}: return considerStrongSpaces(1)
-
-    template considerAsgn(value: untyped) =
-      result = if tok.ident.s[L-1] == '=': 1 else: value
-
-    case relevantChar
-    of '$', '^': considerAsgn(10)
-    of '*', '%', '/', '\\': considerAsgn(9)
-    of '~': result = 8
-    of '+', '-', '|': considerAsgn(8)
-    of '&': considerAsgn(7)
-    of '=', '<', '>', '!': result = 5
-    of '.': considerAsgn(6)
-    of '?': result = 2
-    else: considerAsgn(2)
-  of tkDiv, tkMod, tkShl, tkShr: result = 9
-  of tkIn, tkNotin, tkIs, tkIsnot, tkNot, tkOf, tkAs: result = 5
-  of tkDotDot: result = 6
-  of tkAnd: result = 4
-  of tkOr, tkXor, tkPtr, tkRef: result = 3
-  else: return -10
-  result = considerStrongSpaces(result)
-
-proc isOperator(tok: TToken): bool =
-  ## Determines if the given token is an operator type token.
-  tok.tokType in {tkOpr, tkDiv, tkMod, tkShl, tkShr, tkIn, tkNotin, tkIs,
-                  tkIsnot, tkNot, tkOf, tkAs, tkDotDot, tkAnd, tkOr, tkXor}
-
-proc isUnary(p: TParser): bool =
-  ## Check if the current parser token is a unary operator
-  if p.tok.tokType in {tkOpr, tkDotDot}:
-      result = true
-
-proc checkBinary(p: TParser) {.inline.} =
-  ## Check if the current parser token is a binary operator.
-  # we don't check '..' here as that's too annoying
-  discard
-
-#| module = stmt ^* (';' / IND{=})
-#|
-#| comma = ',' COMMENT?
-#| semicolon = ';' COMMENT?
-#| colon = ':' COMMENT?
-#| colcom = ':' COMMENT?
-#|
-#| operator =  OP0 | OP1 | OP2 | OP3 | OP4 | OP5 | OP6 | OP7 | OP8 | OP9
-#|          | 'or' | 'xor' | 'and'
-#|          | 'is' | 'isnot' | 'in' | 'notin' | 'of'
-#|          | 'div' | 'mod' | 'shl' | 'shr' | 'not' | 'static' | '..'
-#|
-#| prefixOperator = operator
-#|
-#| optInd = COMMENT?
-#| optPar = (IND{>} | IND{=})?
-#|
-#| simpleExpr = arrowExpr (OP0 optInd arrowExpr)*
-#| arrowExpr = assignExpr (OP1 optInd assignExpr)*
-#| assignExpr = orExpr (OP2 optInd orExpr)*
-#| orExpr = andExpr (OP3 optInd andExpr)*
-#| andExpr = cmpExpr (OP4 optInd cmpExpr)*
-#| cmpExpr = sliceExpr (OP5 optInd sliceExpr)*
-#| sliceExpr = ampExpr (OP6 optInd ampExpr)*
-#| ampExpr = plusExpr (OP7 optInd plusExpr)*
-#| plusExpr = mulExpr (OP8 optInd mulExpr)*
-#| mulExpr = dollarExpr (OP9 optInd dollarExpr)*
-#| dollarExpr = primary (OP10 optInd primary)*
-
-proc colcom(p: var TParser, n: PNode) =
-  skipComment(p, n)
-
-proc parseSymbol(p: var TParser, allowNil = false): PNode =
-  #| symbol = '`' (KEYW|IDENT|literal|(operator|'('|')'|'['|']'|'{'|'}'|'=')+)+ '`'
-  #|        | IDENT | 'addr' | 'type'
-  case p.tok.tokType
-  of tkSymbol, tkAddr, tkType:
-    result = newIdentNodeP(p.tok.ident, p)
-    getTok(p)
-  of tkAccent:
-    result = newNodeP(nkAccQuoted, p)
-    getTok(p)
-    while true:
-      case p.tok.tokType
-      of tkAccent:
-        if result.len == 0:
-          parMessage(p, errIdentifierExpected, p.tok)
-        break
-      of tkOpr, tkDot, tkDotDot, tkEquals, tkParLe..tkParDotRi:
-        var accm = ""
-        while p.tok.tokType in {tkOpr, tkDot, tkDotDot, tkEquals,
-                                tkParLe..tkParDotRi}:
-          accm.add(tokToStr(p.tok))
-          getTok(p)
-        result.add(newIdentNodeP(p.lex.cache.getIdent(accm), p))
-      of tokKeywordLow..tokKeywordHigh, tkSymbol, tkIntLit..tkCharLit:
-        result.add(newIdentNodeP(p.lex.cache.getIdent(tokToStr(p.tok)), p))
-        getTok(p)
-      else:
-        parMessage(p, errIdentifierExpected, p.tok)
-        break
-    eat(p, tkAccent)
-  else:
-    if allowNil and p.tok.tokType == tkNil:
-      result = newNodeP(nkNilLit, p)
-      getTok(p)
-    else:
-      parMessage(p, errIdentifierExpected, p.tok)
-      # BUGFIX: We must consume a token here to prevent endless loops!
-      # 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
-
-proc colonOrEquals(p: var TParser, a: PNode): PNode =
-  if p.tok.tokType == tkColon:
-    result = newNodeP(nkExprColonExpr, p)
-    getTok(p)
-    #optInd(p, result)
-    addSon(result, a)
-    addSon(result, parseExpr(p))
-  elif p.tok.tokType == tkEquals:
-    result = newNodeP(nkExprEqExpr, p)
-    getTok(p)
-    #optInd(p, result)
-    addSon(result, a)
-    addSon(result, parseExpr(p))
-  else:
-    result = a
-
-proc exprColonEqExpr(p: var TParser): PNode =
-  #| exprColonEqExpr = expr (':'|'=' expr)?
-  var a = parseExpr(p)
-  result = colonOrEquals(p, a)
-
-proc exprList(p: var TParser, endTok: TTokType, result: PNode) =
-  #| exprList = expr ^+ comma
-  getTok(p)
-  optInd(p, result)
-  while (p.tok.tokType != endTok) and (p.tok.tokType != tkEof):
-    var a = parseExpr(p)
-    addSon(result, a)
-    if p.tok.tokType != tkComma: break
-    getTok(p)
-    optInd(p, a)
-
-proc dotExpr(p: var TParser, a: PNode): PNode =
-  #| dotExpr = expr '.' optInd symbol
-  var info = p.parLineInfo
-  getTok(p)
-  result = newNodeI(nkDotExpr, info)
-  optInd(p, result)
-  addSon(result, a)
-  addSon(result, parseSymbol(p))
-
-proc qualifiedIdent(p: var TParser): PNode =
-  #| qualifiedIdent = symbol ('.' optInd symbol)?
-  result = parseSymbol(p)
-  if p.tok.tokType == tkDot: result = dotExpr(p, result)
-
-proc exprColonEqExprListAux(p: var TParser, endTok: TTokType, result: PNode) =
-  assert(endTok in {tkCurlyLe, tkCurlyRi, tkCurlyDotRi, tkBracketRi, tkParRi})
-  getTok(p)
-  optInd(p, result)
-  while p.tok.tokType != endTok and p.tok.tokType != tkEof:
-    var a = exprColonEqExpr(p)
-    addSon(result, a)
-    if p.tok.tokType != tkComma: break
-    getTok(p)
-    skipComment(p, a)
-  optPar(p)
-  eat(p, endTok)
-
-proc exprColonEqExprList(p: var TParser, kind: TNodeKind,
-                         endTok: TTokType): PNode =
-  #| exprColonEqExprList = exprColonEqExpr (comma exprColonEqExpr)* (comma)?
-  result = newNodeP(kind, p)
-  exprColonEqExprListAux(p, endTok, result)
-
-proc setOrTableConstr(p: var TParser): PNode =
-  result = newNodeP(nkCurly, p)
-  getTok(p)
-  optInd(p, result)
-  if p.tok.tokType == tkColon:
-    getTok(p) # skip ':'
-    result.kind = nkTableConstr
-  else:
-    while p.tok.tokType notin {tkBracketDotRi, tkEof}:
-      var a = exprColonEqExpr(p)
-      if a.kind == nkExprColonExpr: result.kind = nkTableConstr
-      addSon(result, a)
-      if p.tok.tokType != tkComma: break
-      getTok(p)
-      skipComment(p, a)
-  optPar(p)
-  eat(p, tkBracketDotRi)
-
-proc parseCast(p: var TParser): PNode =
-  #| castExpr = 'cast' '[' optInd typeDesc optPar ']' '(' optInd expr optPar ')'
-  result = newNodeP(nkCast, p)
-  getTok(p)
-  eat(p, tkBracketLe)
-  optInd(p, result)
-  addSon(result, parseTypeDesc(p))
-  optPar(p)
-  eat(p, tkBracketRi)
-  eat(p, tkParLe)
-  optInd(p, result)
-  addSon(result, parseExpr(p))
-  optPar(p)
-  eat(p, tkParRi)
-
-proc setBaseFlags(n: PNode, base: TNumericalBase) =
-  case base
-  of base10: discard
-  of base2: incl(n.flags, nfBase2)
-  of base8: incl(n.flags, nfBase8)
-  of base16: incl(n.flags, nfBase16)
-
-proc parseGStrLit(p: var TParser, a: PNode): PNode =
-  case p.tok.tokType
-  of tkGStrLit:
-    result = newNodeP(nkCallStrLit, p)
-    addSon(result, a)
-    addSon(result, newStrNodeP(nkRStrLit, p.tok.literal, p))
-    getTok(p)
-  of tkGTripleStrLit:
-    result = newNodeP(nkCallStrLit, p)
-    addSon(result, a)
-    addSon(result, newStrNodeP(nkTripleStrLit, p.tok.literal, p))
-    getTok(p)
-  else:
-    result = a
-
-type
-  TPrimaryMode = enum pmNormal, pmTypeDesc, pmTypeDef, pmSkipSuffix
-
-proc complexOrSimpleStmt(p: var TParser): PNode
-proc simpleExpr(p: var TParser, mode = pmNormal): PNode
-
-proc semiStmtList(p: var TParser, result: PNode) =
-  inc p.inSemiStmtList
-  result.add(complexOrSimpleStmt(p))
-  while p.tok.tokType == tkSemiColon:
-    getTok(p)
-    optInd(p, result)
-    result.add(complexOrSimpleStmt(p))
-  dec p.inSemiStmtList
-  result.kind = nkStmtListExpr
-
-proc parsePar(p: var TParser): PNode =
-  #| parKeyw = 'discard' | 'include' | 'if' | 'while' | 'case' | 'try'
-  #|         | 'finally' | 'except' | 'for' | 'block' | 'const' | 'let'
-  #|         | 'when' | 'var' | 'mixin'
-  #| par = '(' optInd
-  #|           ( &parKeyw complexOrSimpleStmt ^+ ';'
-  #|           | ';' complexOrSimpleStmt ^+ ';'
-  #|           | pragmaStmt
-  #|           | simpleExpr ( ('=' expr (';' complexOrSimpleStmt ^+ ';' )? )
-  #|                        | (':' expr (',' exprColonEqExpr     ^+ ',' )? ) ) )
-  #|           optPar ')'
-  #
-  # unfortunately it's ambiguous: (expr: expr) vs (exprStmt); however a
-  # leading ';' could be used to enforce a 'stmt' context ...
-  result = newNodeP(nkPar, p)
-  getTok(p)
-  optInd(p, result)
-  if p.tok.tokType in {tkDiscard, tkInclude, tkIf, tkWhile, tkCase,
-                       tkTry, tkDefer, tkFinally, tkExcept, tkFor, tkBlock,
-                       tkConst, tkLet, tkWhen, tkVar,
-                       tkMixin}:
-    # XXX 'bind' used to be an expression, so we exclude it here;
-    # tests/reject/tbind2 fails otherwise.
-    semiStmtList(p, result)
-  elif p.tok.tokType == tkSemiColon:
-    # '(;' enforces 'stmt' context:
-    getTok(p)
-    optInd(p, result)
-    semiStmtList(p, result)
-  elif p.tok.tokType == tkCurlyDotLe:
-    result.add(parseStmtPragma(p))
-  elif p.tok.tokType != tkParRi:
-    var a = simpleExpr(p)
-    if p.tok.tokType == tkEquals:
-      # special case: allow assignments
-      getTok(p)
-      optInd(p, result)
-      let b = parseExpr(p)
-      let asgn = newNodeI(nkAsgn, a.info, 2)
-      asgn.sons[0] = a
-      asgn.sons[1] = b
-      result.add(asgn)
-      if p.tok.tokType == tkSemiColon:
-        semiStmtList(p, result)
-    elif p.tok.tokType == tkSemiColon:
-      # stmt context:
-      result.add(a)
-      semiStmtList(p, result)
-    else:
-      a = colonOrEquals(p, a)
-      result.add(a)
-      if p.tok.tokType == tkComma:
-        getTok(p)
-        skipComment(p, a)
-        while p.tok.tokType != tkParRi and p.tok.tokType != tkEof:
-          var a = exprColonEqExpr(p)
-          addSon(result, a)
-          if p.tok.tokType != tkComma: break
-          getTok(p)
-          skipComment(p, a)
-  optPar(p)
-  eat(p, tkParRi)
-
-proc identOrLiteral(p: var TParser, mode: TPrimaryMode): PNode =
-  #| literal = | INT_LIT | INT8_LIT | INT16_LIT | INT32_LIT | INT64_LIT
-  #|           | UINT_LIT | UINT8_LIT | UINT16_LIT | UINT32_LIT | UINT64_LIT
-  #|           | FLOAT_LIT | FLOAT32_LIT | FLOAT64_LIT
-  #|           | STR_LIT | RSTR_LIT | TRIPLESTR_LIT
-  #|           | CHAR_LIT
-  #|           | NIL
-  #| generalizedLit = GENERALIZED_STR_LIT | GENERALIZED_TRIPLESTR_LIT
-  #| identOrLiteral = generalizedLit | symbol | literal
-  #|                | par | arrayConstr | setOrTableConstr
-  #|                | castExpr
-  #| tupleConstr = '(' optInd (exprColonEqExpr comma?)* optPar ')'
-  #| arrayConstr = '[' optInd (exprColonEqExpr comma?)* optPar ']'
-  case p.tok.tokType
-  of tkSymbol, tkType, tkAddr:
-    result = newIdentNodeP(p.tok.ident, p)
-    getTok(p)
-    result = parseGStrLit(p, result)
-  of tkAccent:
-    result = parseSymbol(p)       # literals
-  of tkIntLit:
-    result = newIntNodeP(nkIntLit, p.tok.iNumber, p)
-    setBaseFlags(result, p.tok.base)
-    getTok(p)
-  of tkInt8Lit:
-    result = newIntNodeP(nkInt8Lit, p.tok.iNumber, p)
-    setBaseFlags(result, p.tok.base)
-    getTok(p)
-  of tkInt16Lit:
-    result = newIntNodeP(nkInt16Lit, p.tok.iNumber, p)
-    setBaseFlags(result, p.tok.base)
-    getTok(p)
-  of tkInt32Lit:
-    result = newIntNodeP(nkInt32Lit, p.tok.iNumber, p)
-    setBaseFlags(result, p.tok.base)
-    getTok(p)
-  of tkInt64Lit:
-    result = newIntNodeP(nkInt64Lit, p.tok.iNumber, p)
-    setBaseFlags(result, p.tok.base)
-    getTok(p)
-  of tkUIntLit:
-    result = newIntNodeP(nkUIntLit, p.tok.iNumber, p)
-    setBaseFlags(result, p.tok.base)
-    getTok(p)
-  of tkUInt8Lit:
-    result = newIntNodeP(nkUInt8Lit, p.tok.iNumber, p)
-    setBaseFlags(result, p.tok.base)
-    getTok(p)
-  of tkUInt16Lit:
-    result = newIntNodeP(nkUInt16Lit, p.tok.iNumber, p)
-    setBaseFlags(result, p.tok.base)
-    getTok(p)
-  of tkUInt32Lit:
-    result = newIntNodeP(nkUInt32Lit, p.tok.iNumber, p)
-    setBaseFlags(result, p.tok.base)
-    getTok(p)
-  of tkUInt64Lit:
-    result = newIntNodeP(nkUInt64Lit, p.tok.iNumber, p)
-    setBaseFlags(result, p.tok.base)
-    getTok(p)
-  of tkFloatLit:
-    result = newFloatNodeP(nkFloatLit, p.tok.fNumber, p)
-    setBaseFlags(result, p.tok.base)
-    getTok(p)
-  of tkFloat32Lit:
-    result = newFloatNodeP(nkFloat32Lit, p.tok.fNumber, p)
-    setBaseFlags(result, p.tok.base)
-    getTok(p)
-  of tkFloat64Lit:
-    result = newFloatNodeP(nkFloat64Lit, p.tok.fNumber, p)
-    setBaseFlags(result, p.tok.base)
-    getTok(p)
-  of tkFloat128Lit:
-    result = newFloatNodeP(nkFloat128Lit, p.tok.fNumber, p)
-    setBaseFlags(result, p.tok.base)
-    getTok(p)
-  of tkStrLit:
-    result = newStrNodeP(nkStrLit, p.tok.literal, p)
-    getTok(p)
-  of tkRStrLit:
-    result = newStrNodeP(nkRStrLit, p.tok.literal, p)
-    getTok(p)
-  of tkTripleStrLit:
-    result = newStrNodeP(nkTripleStrLit, p.tok.literal, p)
-    getTok(p)
-  of tkCharLit:
-    result = newIntNodeP(nkCharLit, ord(p.tok.literal[0]), p)
-    getTok(p)
-  of tkNil:
-    result = newNodeP(nkNilLit, p)
-    getTok(p)
-  of tkParLe:
-    # () constructor
-    if mode in {pmTypeDesc, pmTypeDef}:
-      result = exprColonEqExprList(p, nkPar, tkParRi)
-    else:
-      result = parsePar(p)
-  of tkBracketDotLe:
-    # {} constructor
-    result = setOrTableConstr(p)
-  of tkBracketLe:
-    # [] constructor
-    result = exprColonEqExprList(p, nkBracket, tkBracketRi)
-  of tkCast:
-    result = parseCast(p)
-  else:
-    parMessage(p, errExprExpected, p.tok)
-    getTok(p)  # we must consume a token here to prevend endless loops!
-    result = ast.emptyNode
-
-proc namedParams(p: var TParser, callee: PNode,
-                 kind: TNodeKind, endTok: TTokType): PNode =
-  let a = callee
-  result = newNodeP(kind, p)
-  addSon(result, a)
-  exprColonEqExprListAux(p, endTok, result)
-
-proc parseMacroColon(p: var TParser, x: PNode): PNode
-proc primarySuffix(p: var TParser, r: PNode): PNode =
-  #| primarySuffix = '(' (exprColonEqExpr comma?)* ')' doBlocks?
-  #|       | doBlocks
-  #|       | '.' optInd symbol generalizedLit?
-  #|       | '[' optInd indexExprList optPar ']'
-  #|       | '{' optInd indexExprList optPar '}'
-  #|       | &( '`'|IDENT|literal|'cast'|'addr'|'type') expr # command syntax
-  result = r
-
-  template somePar() = discard
-  while p.tok.indent < 0:
-    case p.tok.tokType
-    of tkParLe:
-      somePar()
-      result = namedParams(p, result, nkCall, tkParRi)
-      if result.len > 1 and result.sons[1].kind == nkExprColonExpr:
-        result.kind = nkObjConstr
-      else:
-        parseDoBlocks(p, result)
-    of tkDo:
-      var a = result
-      result = newNodeP(nkCall, p)
-      addSon(result, a)
-      parseDoBlocks(p, result)
-    of tkDot:
-      result = dotExpr(p, result)
-      result = parseGStrLit(p, result)
-    of tkBracketLe:
-      somePar()
-      result = namedParams(p, result, nkBracketExpr, tkBracketRi)
-    of tkBracketDotLe:
-      somePar()
-      result = namedParams(p, result, nkCurlyExpr, tkBracketDotRi)
-    of tkSymbol, tkAccent, tkIntLit..tkCharLit, tkNil, tkCast, tkAddr, tkType:
-      if p.inPragma == 0:
-        # actually parsing {.push hints:off.} as {.push(hints:off).} is a sweet
-        # solution, but pragmas.nim can't handle that
-        let a = result
-        result = newNodeP(nkCommand, p)
-        addSon(result, a)
-        when true:
-          addSon result, parseExpr(p)
-        else:
-          while p.tok.tokType != tkEof:
-            let x = parseExpr(p)
-            addSon(result, x)
-            if p.tok.tokType != tkComma: break
-            getTok(p)
-            optInd(p, x)
-          if p.tok.tokType == tkDo:
-            parseDoBlocks(p, result)
-          else:
-            result = parseMacroColon(p, result)
-      break
-    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
-  # expand while operators have priorities higher than 'limit'
-  var opPrec = getPrecedence(p.tok)
-  let modeB = if mode == pmTypeDef: pmTypeDesc else: mode
-  # the operator itself must not start on a new line:
-  while opPrec >= limit and p.tok.indent < 0 and not isAt(p.tok):
-    checkBinary(p)
-    var leftAssoc = 1-ord(isRightAssociative(p.tok))
-    var a = newNodeP(nkInfix, p)
-    var opNode = newIdentNodeP(p.tok.ident, p) # skip operator:
-    getTok(p)
-    optInd(p, a)
-    # read sub-expression with higher priority:
-    var b = simpleExprAux(p, opPrec + leftAssoc, modeB)
-    addSon(a, opNode)
-    addSon(a, result)
-    addSon(a, b)
-    result = a
-    opPrec = getPrecedence(p.tok)
-
-proc simpleExprAux(p: var TParser, limit: int, mode: TPrimaryMode): PNode =
-  result = primary(p, mode)
-  result = parseOperators(p, result, limit, mode)
-
-proc simpleExpr(p: var TParser, mode = pmNormal): PNode =
-  result = simpleExprAux(p, -1, mode)
-
-proc parseIfExpr(p: var TParser, kind: TNodeKind): PNode =
-  #| condExpr = expr colcom expr optInd
-  #|         ('elif' expr colcom expr optInd)*
-  #|          'else' colcom expr
-  #| ifExpr = 'if' condExpr
-  #| whenExpr = 'when' condExpr
-  result = newNodeP(kind, p)
-  while true:
-    getTok(p)                 # skip `if`, `elif`
-    var branch = newNodeP(nkElifExpr, p)
-    addSon(branch, parseExpr(p))
-    colcom(p, branch)
-    addSon(branch, parseExpr(p))
-    optInd(p, branch)
-    addSon(result, branch)
-    if p.tok.tokType != tkElif: break
-  var branch = newNodeP(nkElseExpr, p)
-  eat(p, tkElse)
-  colcom(p, branch)
-  addSon(branch, parseExpr(p))
-  addSon(result, branch)
-
-proc parsePragma(p: var TParser): PNode =
-  result = newNodeP(nkPragma, p)
-  inc p.inPragma
-  if isAt(p.tok):
-    while isAt(p.tok):
-      getTok(p)
-      var a = parseExpr(p)
-      optInd(p, a)
-      if a.kind in nkCallKinds and a.len == 2:
-        let repaired = newNodeI(nkExprColonExpr, a.info)
-        repaired.add a[0]
-        repaired.add a[1]
-        a = repaired
-      addSon(result, a)
-      skipComment(p, a)
-  else:
-    getTok(p)
-    optInd(p, result)
-    while p.tok.tokType notin {tkCurlyDotRi, tkCurlyRi, tkEof}:
-      var a = exprColonEqExpr(p)
-      addSon(result, a)
-      if p.tok.tokType == tkComma:
-        getTok(p)
-        skipComment(p, a)
-    optPar(p)
-    if p.tok.tokType in {tkCurlyDotRi, tkCurlyRi}: getTok(p)
-    else: parMessage(p, errTokenExpected, ".}")
-  dec p.inPragma
-
-proc identVis(p: var TParser; allowDot=false): PNode =
-  #| identVis = symbol opr?  # postfix position
-  #| identVisDot = symbol '.' optInd symbol opr?
-  var a = parseSymbol(p)
-  if p.tok.tokType == tkOpr:
-    result = newNodeP(nkPostfix, p)
-    addSon(result, newIdentNodeP(p.tok.ident, p))
-    addSon(result, a)
-    getTok(p)
-  elif p.tok.tokType == tkDot and allowDot:
-    result = dotExpr(p, a)
-  else:
-    result = a
-
-proc identWithPragma(p: var TParser; allowDot=false): PNode =
-  #| identWithPragma = identVis pragma?
-  #| identWithPragmaDot = identVisDot pragma?
-  var a = identVis(p, allowDot)
-  if p.tok.tokType == tkCurlyDotLe or isAt(p.tok):
-    result = newNodeP(nkPragmaExpr, p)
-    addSon(result, a)
-    addSon(result, parsePragma(p))
-  else:
-    result = a
-
-type
-  TDeclaredIdentFlag = enum
-    withPragma,               # identifier may have pragma
-    withBothOptional          # both ':' and '=' parts are optional
-  TDeclaredIdentFlags = set[TDeclaredIdentFlag]
-
-proc parseIdentColonEquals(p: var TParser, flags: TDeclaredIdentFlags): PNode =
-  #| declColonEquals = identWithPragma (comma identWithPragma)* comma?
-  #|                   (':' optInd typeDesc)? ('=' optInd expr)?
-  #| identColonEquals = ident (comma ident)* comma?
-  #|      (':' optInd typeDesc)? ('=' optInd expr)?)
-  var a: PNode
-  result = newNodeP(nkIdentDefs, p)
-  while true:
-    case p.tok.tokType
-    of tkSymbol, tkAccent:
-      if withPragma in flags: a = identWithPragma(p)
-      else: a = parseSymbol(p)
-      if a.kind == nkEmpty: return
-    else: break
-    addSon(result, a)
-    if p.tok.tokType != tkComma: break
-    getTok(p)
-    optInd(p, a)
-  if p.tok.tokType == tkColon:
-    getTok(p)
-    optInd(p, result)
-    addSon(result, parseTypeDesc(p))
-  else:
-    addSon(result, ast.emptyNode)
-    if p.tok.tokType != tkEquals and withBothOptional notin flags:
-      parMessage(p, errColonOrEqualsExpected, p.tok)
-  if p.tok.tokType == tkEquals:
-    getTok(p)
-    optInd(p, result)
-    addSon(result, parseExpr(p))
-  else:
-    addSon(result, ast.emptyNode)
-
-proc parseTuple(p: var TParser): PNode =
-  result = newNodeP(nkTupleTy, p)
-  getTok(p)
-  if p.tok.tokType in {tkBracketLe, tkCurlyLe}:
-    let usedCurly = p.tok.tokType == tkCurlyLe
-    getTok(p)
-    optInd(p, result)
-    while p.tok.tokType in {tkSymbol, tkAccent}:
-      var a = parseIdentColonEquals(p, {})
-      addSon(result, a)
-      if p.tok.tokType notin {tkComma, tkSemiColon}: break
-      getTok(p)
-      skipComment(p, a)
-    optPar(p)
-    if usedCurly: eat(p, tkCurlyRi)
-    else: eat(p, tkBracketRi)
-  else:
-    result = newNodeP(nkTupleClassTy, p)
-
-proc parseParamList(p: var TParser, retColon = true): PNode =
-  #| paramList = '(' declColonEquals ^* (comma/semicolon) ')'
-  #| paramListArrow = paramList? ('->' optInd typeDesc)?
-  #| paramListColon = paramList? (':' optInd typeDesc)?
-  var a: PNode
-  result = newNodeP(nkFormalParams, p)
-  addSon(result, ast.emptyNode) # return type
-  let hasParLe = p.tok.tokType == tkParLe and p.tok.indent < 0
-  if hasParLe:
-    getTok(p)
-    optInd(p, result)
-    while true:
-      case p.tok.tokType
-      of tkSymbol, tkAccent:
-        a = parseIdentColonEquals(p, {withBothOptional, withPragma})
-      of tkParRi:
-        break
-      else:
-        parMessage(p, errTokenExpected, ")")
-        break
-      addSon(result, a)
-      if p.tok.tokType notin {tkComma, tkSemiColon}: break
-      getTok(p)
-      skipComment(p, a)
-    optPar(p)
-    eat(p, tkParRi)
-  let hasRet = if retColon: p.tok.tokType == tkColon
-               else: p.tok.tokType == tkOpr and p.tok.ident.s == "->"
-  if hasRet and p.tok.indent < 0:
-    getTok(p)
-    optInd(p, result)
-    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
-
-proc optPragmas(p: var TParser): PNode =
-  if p.tok.tokType == tkCurlyDotLe or isAt(p.tok):
-    result = parsePragma(p)
-  else:
-    result = ast.emptyNode
-
-proc parseDoBlock(p: var TParser): PNode =
-  #| doBlock = 'do' paramListArrow pragmas? colcom stmt
-  let info = parLineInfo(p)
-  getTok(p)
-  let params = parseParamList(p, retColon=false)
-  let pragmas = optPragmas(p)
-  colcom(p, result)
-  result = newProcNode(nkDo, info, parseStmt(p),
-                       params = params,
-                       pragmas = pragmas)
-
-proc parseDoBlocks(p: var TParser, call: PNode) =
-  #| doBlocks = doBlock ^* IND{=}
-  while p.tok.tokType == tkDo:
-    addSon(call, parseDoBlock(p))
-
-proc parseCurlyStmt(p: var TParser): PNode =
-  result = newNodeP(nkStmtList, p)
-  eat(p, tkCurlyLe)
-  result.add parseStmt(p)
-  while p.tok.tokType notin {tkEof, tkCurlyRi}:
-    if p.tok.tokType == tkSemicolon: getTok(p)
-    elif p.tok.indent < 0: break
-    result.add parseStmt(p)
-  eat(p, tkCurlyRi)
-
-proc parseProcExpr(p: var TParser, isExpr: bool): PNode =
-  #| procExpr = 'proc' paramListColon pragmas? ('=' COMMENT? stmt)?
-  # either a proc type or a anonymous proc
-  let info = parLineInfo(p)
-  getTok(p)
-  let hasSignature = p.tok.tokType in {tkParLe, tkColon} and p.tok.indent < 0
-  let params = parseParamList(p)
-  let pragmas = optPragmas(p)
-  if p.tok.tokType == tkCurlyLe and isExpr:
-    result = newProcNode(nkLambda, info, parseCurlyStmt(p),
-                         params = params,
-                         pragmas = pragmas)
-  else:
-    result = newNodeI(nkProcTy, info)
-    if hasSignature:
-      addSon(result, params)
-      addSon(result, pragmas)
-
-proc isExprStart(p: TParser): bool =
-  case p.tok.tokType
-  of tkSymbol, tkAccent, tkOpr, tkNot, tkNil, tkCast, tkIf,
-     tkProc, tkIterator, tkBind, tkAddr,
-     tkParLe, tkBracketLe, tkCurlyLe, tkIntLit..tkCharLit, tkVar, tkRef, tkPtr,
-     tkTuple, tkObject, tkType, tkWhen, tkCase, tkOut:
-    result = true
-  else: result = false
-
-proc parseSymbolList(p: var TParser, result: PNode, allowNil = false) =
-  while true:
-    var s = parseSymbol(p, allowNil)
-    if s.kind == nkEmpty: break
-    addSon(result, s)
-    if p.tok.tokType != tkComma: break
-    getTok(p)
-    optInd(p, s)
-
-proc parseTypeDescKAux(p: var TParser, kind: TNodeKind,
-                       mode: TPrimaryMode): PNode =
-  #| distinct = 'distinct' optInd typeDesc
-  result = newNodeP(kind, p)
-  getTok(p)
-  optInd(p, result)
-  if not isOperator(p.tok) and isExprStart(p):
-    addSon(result, primary(p, mode))
-  if kind == nkDistinctTy and p.tok.tokType == tkSymbol:
-    var nodeKind: TNodeKind
-    if p.tok.ident.s == "with":
-      nodeKind = nkWith
-    elif p.tok.ident.s == "without":
-      nodeKind = nkWithout
-    else:
-      return result
-    getTok(p)
-    let list = newNodeP(nodeKind, p)
-    result.addSon list
-    parseSymbolList(p, list, allowNil = true)
-
-proc parseExpr(p: var TParser): PNode =
-  #| expr = (ifExpr
-  #|       | whenExpr
-  #|       | caseExpr
-  #|       | tryExpr)
-  #|       / simpleExpr
-  case p.tok.tokType:
-  of tkIf: result = parseIfExpr(p, nkIfExpr)
-  of tkWhen: result = parseIfExpr(p, nkWhenExpr)
-  of tkCase: result = parseCase(p)
-  of tkTry: result = parseTry(p)
-  else: result = simpleExpr(p)
-
-proc parseEnum(p: var TParser): PNode
-proc parseObject(p: var TParser): PNode
-proc parseTypeClass(p: var TParser): PNode
-
-proc primary(p: var TParser, mode: TPrimaryMode): PNode =
-  #| typeKeyw = 'var' | 'out' | 'ref' | 'ptr' | 'shared' | 'tuple'
-  #|          | 'proc' | 'iterator' | 'distinct' | 'object' | 'enum'
-  #| primary = typeKeyw typeDescK
-  #|         /  prefixOperator* identOrLiteral primarySuffix*
-  #|         / 'static' primary
-  #|         / 'bind' primary
-  if isOperator(p.tok):
-    let isSigil = isSigilLike(p.tok)
-    result = newNodeP(nkPrefix, p)
-    var a = newIdentNodeP(p.tok.ident, p)
-    addSon(result, a)
-    getTok(p)
-    optInd(p, a)
-    if isSigil:
-      #XXX prefix operators
-      addSon(result, primary(p, pmSkipSuffix))
-      result = primarySuffix(p, result)
-    else:
-      addSon(result, primary(p, pmNormal))
-    return
-
-  case p.tok.tokType:
-  of tkTuple: result = parseTuple(p)
-  of tkProc: result = parseProcExpr(p, mode notin {pmTypeDesc, pmTypeDef})
-  of tkIterator:
-    result = parseProcExpr(p, mode notin {pmTypeDesc, pmTypeDef})
-    if result.kind == nkLambda: result.kind = nkIteratorDef
-    else: result.kind = nkIteratorTy
-  of tkEnum:
-    if mode == pmTypeDef:
-      result = parseEnum(p)
-    else:
-      result = newNodeP(nkEnumTy, p)
-      getTok(p)
-  of tkObject:
-    if mode == pmTypeDef:
-      result = parseObject(p)
-    else:
-      result = newNodeP(nkObjectTy, p)
-      getTok(p)
-  of tkConcept:
-    if mode == pmTypeDef:
-      result = parseTypeClass(p)
-    else:
-      parMessage(p, errInvalidToken, p.tok)
-  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)
-    optInd(p, result)
-    addSon(result, primary(p, pmNormal))
-  of tkVar: result = parseTypeDescKAux(p, nkVarTy, mode)
-  of tkOut: result = parseTypeDescKAux(p, nkVarTy, mode)
-  of tkRef: result = parseTypeDescKAux(p, nkRefTy, mode)
-  of tkPtr: result = parseTypeDescKAux(p, nkPtrTy, mode)
-  of tkDistinct: result = parseTypeDescKAux(p, nkDistinctTy, mode)
-  else:
-    result = identOrLiteral(p, mode)
-    if mode != pmSkipSuffix:
-      result = primarySuffix(p, result)
-
-proc parseTypeDesc(p: var TParser): PNode =
-  #| typeDesc = simpleExpr
-  result = simpleExpr(p, pmTypeDesc)
-
-proc parseTypeDefAux(p: var TParser): PNode =
-  #| typeDefAux = simpleExpr
-  #|            | 'concept' typeClass
-  result = simpleExpr(p, pmTypeDef)
-
-proc makeCall(n: PNode): PNode =
-  ## Creates a call if the given node isn't already a call.
-  if n.kind in nkCallKinds:
-    result = n
-  else:
-    result = newNodeI(nkCall, n.info)
-    result.add n
-
-proc parseMacroColon(p: var TParser, x: PNode): PNode =
-  #| macroColon = ':' stmt? ( IND{=} 'of' exprList ':' stmt
-  #|                        | IND{=} 'elif' expr ':' stmt
-  #|                        | IND{=} 'except' exprList ':' stmt
-  #|                        | IND{=} 'else' ':' stmt )*
-  result = x
-  if p.tok.tokType == tkColon and p.tok.indent < 0:
-    result = makeCall(result)
-    getTok(p)
-    skipComment(p, result)
-    let stmtList = newNodeP(nkStmtList, p)
-    if p.tok.tokType notin {tkOf, tkElif, tkElse, tkExcept}:
-      let body = parseStmt(p)
-      stmtList.add body
-      #addSon(result, makeStmtList(body))
-    while true:
-      var b: PNode
-      case p.tok.tokType
-      of tkOf:
-        b = newNodeP(nkOfBranch, p)
-        exprList(p, tkCurlyLe, b)
-      of tkElif:
-        b = newNodeP(nkElifBranch, p)
-        getTok(p)
-        optInd(p, b)
-        addSon(b, parseExpr(p))
-      of tkExcept:
-        b = newNodeP(nkExceptBranch, p)
-        exprList(p, tkCurlyLe, b)
-      of tkElse:
-        b = newNodeP(nkElse, p)
-        getTok(p)
-      else: break
-      addSon(b, parseCurlyStmt(p))
-      addSon(stmtList, b)
-      if b.kind == nkElse: break
-    if stmtList.len == 1 and stmtList[0].kind == nkStmtList:
-      # to keep backwards compatibility (see tests/vm/tstringnil)
-      result.add stmtList[0]
-    else:
-      result.add stmtList
-
-proc parseExprStmt(p: var TParser): PNode =
-  #| exprStmt = simpleExpr
-  #|          (( '=' optInd expr )
-  #|          / ( expr ^+ comma
-  #|              doBlocks
-  #|               / macroColon
-  #|            ))?
-  var a = simpleExpr(p)
-  if p.tok.tokType == tkEquals:
-    getTok(p)
-    optInd(p, result)
-    var b = parseExpr(p)
-    result = newNodeI(nkAsgn, a.info)
-    addSon(result, a)
-    addSon(result, b)
-  else:
-    # simpleExpr parsed 'p a' from 'p a, b'?
-    if p.tok.indent < 0 and p.tok.tokType == tkComma and a.kind == nkCommand:
-      result = a
-      while true:
-        getTok(p)
-        optInd(p, result)
-        var e = parseExpr(p)
-        addSon(result, e)
-        if p.tok.tokType != tkComma: break
-    elif p.tok.indent < 0 and isExprStart(p):
-      if a.kind == nkCommand:
-        result = a
-      else:
-        result = newNode(nkCommand, a.info, @[a])
-      while true:
-        var e = parseExpr(p)
-        addSon(result, e)
-        if p.tok.tokType != tkComma: break
-        getTok(p)
-        optInd(p, result)
-    else:
-      result = a
-    if p.tok.tokType == tkDo and p.tok.indent < 0:
-      result = makeCall(result)
-      parseDoBlocks(p, result)
-      return result
-    result = parseMacroColon(p, result)
-
-proc parseModuleName(p: var TParser, kind: TNodeKind): PNode =
-  result = parseExpr(p)
-
-proc parseImport(p: var TParser, kind: TNodeKind): PNode =
-  #| importStmt = 'import' optInd expr
-  #|               ((comma expr)*
-  #|               / 'except' optInd (expr ^+ comma))
-  result = newNodeP(kind, p)
-  getTok(p)                   # skip `import` or `export`
-  optInd(p, result)
-  var a = parseModuleName(p, kind)
-  addSon(result, a)
-  if p.tok.tokType in {tkComma, tkExcept}:
-    if p.tok.tokType == tkExcept:
-      result.kind = succ(kind)
-    getTok(p)
-    optInd(p, result)
-    while true:
-      # was: while p.tok.tokType notin {tkEof, tkSad, tkDed}:
-      a = parseModuleName(p, kind)
-      if a.kind == nkEmpty: break
-      addSon(result, a)
-      if p.tok.tokType != tkComma: break
-      getTok(p)
-      optInd(p, a)
-  #expectNl(p)
-
-proc parseIncludeStmt(p: var TParser): PNode =
-  #| includeStmt = 'include' optInd expr ^+ comma
-  result = newNodeP(nkIncludeStmt, p)
-  getTok(p)                   # skip `import` or `include`
-  optInd(p, result)
-  while true:
-    # was: while p.tok.tokType notin {tkEof, tkSad, tkDed}:
-    var a = parseExpr(p)
-    if a.kind == nkEmpty: break
-    addSon(result, a)
-    if p.tok.tokType != tkComma: break
-    getTok(p)
-    optInd(p, a)
-  #expectNl(p)
-
-proc parseFromStmt(p: var TParser): PNode =
-  #| fromStmt = 'from' moduleName 'import' optInd expr (comma expr)*
-  result = newNodeP(nkFromStmt, p)
-  getTok(p)                   # skip `from`
-  optInd(p, result)
-  var a = parseModuleName(p, nkImportStmt)
-  addSon(result, a)           #optInd(p, a);
-  eat(p, tkImport)
-  optInd(p, result)
-  while true:
-    # p.tok.tokType notin {tkEof, tkSad, tkDed}:
-    a = parseExpr(p)
-    if a.kind == nkEmpty: break
-    addSon(result, a)
-    if p.tok.tokType != tkComma: break
-    getTok(p)
-    optInd(p, a)
-  #expectNl(p)
-
-proc parseReturnOrRaise(p: var TParser, kind: TNodeKind): PNode =
-  #| returnStmt = 'return' optInd expr?
-  #| raiseStmt = 'raise' optInd expr?
-  #| yieldStmt = 'yield' optInd expr?
-  #| discardStmt = 'discard' optInd expr?
-  #| breakStmt = 'break' optInd expr?
-  #| continueStmt = 'break' optInd expr?
-  result = newNodeP(kind, p)
-  getTok(p)
-  if p.tok.tokType == tkComment:
-    skipComment(p, result)
-    addSon(result, ast.emptyNode)
-  elif p.tok.indent >= 0 or not isExprStart(p):
-    # NL terminates:
-    addSon(result, ast.emptyNode)
-  else:
-    addSon(result, parseExpr(p))
-
-proc parseIfOrWhen(p: var TParser, kind: TNodeKind): PNode =
-  #| condStmt = expr colcom stmt COMMENT?
-  #|            (IND{=} 'elif' expr colcom stmt)*
-  #|            (IND{=} 'else' colcom stmt)?
-  #| ifStmt = 'if' condStmt
-  #| whenStmt = 'when' condStmt
-  result = newNodeP(kind, p)
-  while true:
-    getTok(p)                 # skip `if`, `when`, `elif`
-    var branch = newNodeP(nkElifBranch, p)
-    optInd(p, branch)
-    addSon(branch, parseExpr(p))
-    colcom(p, branch)
-    addSon(branch, parseCurlyStmt(p))
-    skipComment(p, branch)
-    addSon(result, branch)
-    if p.tok.tokType != tkElif: break
-  if p.tok.tokType == tkElse:
-    var branch = newNodeP(nkElse, p)
-    eat(p, tkElse)
-    addSon(branch, parseCurlyStmt(p))
-    addSon(result, branch)
-
-proc parseWhile(p: var TParser): PNode =
-  #| whileStmt = 'while' expr colcom stmt
-  result = newNodeP(nkWhileStmt, p)
-  getTok(p)
-  optInd(p, result)
-  addSon(result, parseExpr(p))
-  colcom(p, result)
-  addSon(result, parseCurlyStmt(p))
-
-proc parseCase(p: var TParser): PNode =
-  #| ofBranch = 'of' exprList colcom stmt
-  #| ofBranches = ofBranch (IND{=} ofBranch)*
-  #|                       (IND{=} 'elif' expr colcom stmt)*
-  #|                       (IND{=} 'else' colcom stmt)?
-  #| caseStmt = 'case' expr ':'? COMMENT?
-  #|             (IND{>} ofBranches DED
-  #|             | IND{=} ofBranches)
-  var
-    b: PNode
-    inElif= false
-  result = newNodeP(nkCaseStmt, p)
-  getTok(p)
-  addSon(result, parseExpr(p))
-  eat(p, tkCurlyLe)
-  skipComment(p, result)
-
-  while true:
-    case p.tok.tokType
-    of tkOf:
-      if inElif: break
-      b = newNodeP(nkOfBranch, p)
-      exprList(p, tkCurlyLe, b)
-    of tkElif:
-      inElif = true
-      b = newNodeP(nkElifBranch, p)
-      getTok(p)
-      optInd(p, b)
-      addSon(b, parseExpr(p))
-    of tkElse:
-      b = newNodeP(nkElse, p)
-      getTok(p)
-    else: break
-    skipComment(p, b)
-    addSon(b, parseCurlyStmt(p))
-    addSon(result, b)
-    if b.kind == nkElse: break
-  eat(p, tkCurlyRi)
-
-proc parseTry(p: var TParser): PNode =
-  #| tryStmt = 'try' colcom stmt &(IND{=}? 'except'|'finally')
-  #|            (IND{=}? 'except' exprList colcom stmt)*
-  #|            (IND{=}? 'finally' colcom stmt)?
-  #| tryExpr = 'try' colcom stmt &(optInd 'except'|'finally')
-  #|            (optInd 'except' exprList colcom stmt)*
-  #|            (optInd 'finally' colcom stmt)?
-  result = newNodeP(nkTryStmt, p)
-  getTok(p)
-  colcom(p, result)
-  addSon(result, parseCurlyStmt(p))
-  var b: PNode = nil
-  while true:
-    case p.tok.tokType
-    of tkExcept:
-      b = newNodeP(nkExceptBranch, p)
-      exprList(p, tkCurlyLe, b)
-    of tkFinally:
-      b = newNodeP(nkFinally, p)
-      getTok(p)
-    else: break
-    skipComment(p, b)
-    addSon(b, parseCurlyStmt(p))
-    addSon(result, b)
-    if b.kind == nkFinally: break
-  if b == nil: parMessage(p, errTokenExpected, "except")
-
-proc parseFor(p: var TParser): PNode =
-  #| forStmt = 'for' (identWithPragma ^+ comma) 'in' expr colcom stmt
-  result = newNodeP(nkForStmt, p)
-  getTokNoInd(p)
-  var a = identWithPragma(p)
-  addSon(result, a)
-  while p.tok.tokType == tkComma:
-    getTok(p)
-    optInd(p, a)
-    a = identWithPragma(p)
-    addSon(result, a)
-  eat(p, tkIn)
-  addSon(result, parseExpr(p))
-  colcom(p, result)
-  addSon(result, parseCurlyStmt(p))
-
-proc parseBlock(p: var TParser): PNode =
-  #| blockStmt = 'block' symbol? colcom stmt
-  result = newNodeP(nkBlockStmt, p)
-  getTokNoInd(p)
-  if p.tok.tokType == tkCurlyLe: addSon(result, ast.emptyNode)
-  else: addSon(result, parseSymbol(p))
-  colcom(p, result)
-  addSon(result, parseCurlyStmt(p))
-
-proc parseStaticOrDefer(p: var TParser; k: TNodeKind): PNode =
-  #| staticStmt = 'static' colcom stmt
-  #| deferStmt = 'defer' colcom stmt
-  result = newNodeP(k, p)
-  getTok(p)
-  colcom(p, result)
-  addSon(result, parseCurlyStmt(p))
-
-proc parseAsm(p: var TParser): PNode =
-  #| asmStmt = 'asm' pragma? (STR_LIT | RSTR_LIT | TRIPLE_STR_LIT)
-  result = newNodeP(nkAsmStmt, p)
-  getTokNoInd(p)
-  if p.tok.tokType == tkCurlyDotLe or isAt(p.tok): addSon(result, parsePragma(p))
-  else: addSon(result, ast.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))
-  of tkTripleStrLit: addSon(result,
-                            newStrNodeP(nkTripleStrLit, p.tok.literal, p))
-  else:
-    parMessage(p, errStringLiteralExpected)
-    addSon(result, ast.emptyNode)
-    return
-  getTok(p)
-
-proc parseGenericParam(p: var TParser): PNode =
-  #| genericParam = symbol (comma symbol)* (colon expr)? ('=' optInd expr)?
-  var a: PNode
-  result = newNodeP(nkIdentDefs, p)
-  while true:
-    case p.tok.tokType
-    of tkIn, tkOut:
-      let t = p.tok.tokType
-      getTok(p)
-      expectIdent(p)
-      a = parseSymbol(p)
-    of tkSymbol, tkAccent:
-      a = parseSymbol(p)
-      if a.kind == nkEmpty: return
-    else: break
-    addSon(result, a)
-    if p.tok.tokType != tkComma: break
-    getTok(p)
-    optInd(p, a)
-  if p.tok.tokType == tkColon:
-    getTok(p)
-    optInd(p, result)
-    addSon(result, parseExpr(p))
-  else:
-    addSon(result, ast.emptyNode)
-  if p.tok.tokType == tkEquals:
-    getTok(p)
-    optInd(p, result)
-    addSon(result, parseExpr(p))
-  else:
-    addSon(result, ast.emptyNode)
-
-proc parseGenericParamList(p: var TParser): PNode =
-  #| genericParamList = '[' optInd
-  #|   genericParam ^* (comma/semicolon) optPar ']'
-  result = newNodeP(nkGenericParams, p)
-  getTok(p)
-  optInd(p, result)
-  while p.tok.tokType in {tkSymbol, tkAccent}:
-    var a = parseGenericParam(p)
-    addSon(result, a)
-    if p.tok.tokType notin {tkComma, tkSemiColon}: break
-    getTok(p)
-    skipComment(p, a)
-  optPar(p)
-  eat(p, tkBracketRi)
-
-proc parsePattern(p: var TParser): PNode =
-  eat(p, tkBracketDotLe)
-  result = parseStmt(p)
-  eat(p, tkBracketDotRi)
-
-proc validInd(p: TParser): bool = p.tok.indent < 0
-
-proc parseRoutine(p: var TParser, kind: TNodeKind): PNode =
-  #| indAndComment = (IND{>} COMMENT)? | COMMENT?
-  #| routine = optInd identVis pattern? genericParamList?
-  #|   paramListColon pragma? ('=' COMMENT? stmt)? indAndComment
-  result = newNodeP(kind, p)
-  getTok(p)
-  optInd(p, result)
-  addSon(result, identVis(p))
-  if p.tok.tokType == tkBracketDotLe and p.validInd:
-    addSon(result, p.parsePattern)
-  else:
-    addSon(result, ast.emptyNode)
-  if p.tok.tokType == tkBracketLe and p.validInd:
-    result.add(p.parseGenericParamList)
-  else:
-    addSon(result, ast.emptyNode)
-  addSon(result, p.parseParamList)
-  if (p.tok.tokType == tkCurlyDotLe or isAt(p.tok)) and p.validInd:
-    addSon(result, p.parsePragma)
-  else:
-    addSon(result, ast.emptyNode)
-  # empty exception tracking:
-  addSon(result, ast.emptyNode)
-  if p.tok.tokType == tkCurlyLe:
-    addSon(result, parseCurlyStmt(p))
-  else:
-    addSon(result, ast.emptyNode)
-  indAndComment(p, result)
-
-proc newCommentStmt(p: var TParser): PNode =
-  #| commentStmt = COMMENT
-  result = newNodeP(nkCommentStmt, p)
-  result.comment = p.tok.literal
-  getTok(p)
-
-type
-  TDefParser = proc (p: var TParser): PNode {.nimcall.}
-
-proc parseSection(p: var TParser, kind: TNodeKind,
-                  defparser: TDefParser): PNode =
-  #| section(p) = COMMENT? p / (IND{>} (p / COMMENT)^+IND{=} DED)
-  result = newNodeP(kind, p)
-  if kind != nkTypeSection: getTok(p)
-  skipComment(p, result)
-  if p.tok.tokType == tkParLe:
-    getTok(p)
-    skipComment(p, result)
-    while true:
-      case p.tok.tokType
-      of tkSymbol, tkAccent, tkParLe:
-        var a = defparser(p)
-        skipComment(p, a)
-        addSon(result, a)
-      of tkComment:
-        var a = newCommentStmt(p)
-        addSon(result, a)
-      of tkParRi: break
-      else:
-        parMessage(p, errIdentifierExpected, p.tok)
-        break
-    eat(p, tkParRi)
-    if result.len == 0: parMessage(p, errIdentifierExpected, p.tok)
-  elif p.tok.tokType in {tkSymbol, tkAccent, tkBracketLe}:
-    # tkBracketLe is allowed for ``var [x, y] = ...`` tuple parsing
-    addSon(result, defparser(p))
-  else:
-    parMessage(p, errIdentifierExpected, p.tok)
-
-proc parseConstant(p: var TParser): PNode =
-  #| constant = identWithPragma (colon typedesc)? '=' optInd expr indAndComment
-  result = newNodeP(nkConstDef, p)
-  addSon(result, identWithPragma(p))
-  if p.tok.tokType == tkColon:
-    getTok(p)
-    optInd(p, result)
-    addSon(result, parseTypeDesc(p))
-  else:
-    addSon(result, ast.emptyNode)
-  eat(p, tkEquals)
-  optInd(p, result)
-  addSon(result, parseExpr(p))
-  indAndComment(p, result)
-
-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)
-  optInd(p, result)
-  flexComment(p, result)
-  eat(p, tkCurlyLe)
-  optInd(p, result)
-  while p.tok.tokType notin {tkEof, tkCurlyRi}:
-    var a = parseSymbol(p)
-    if a.kind == nkEmpty: return
-    if p.tok.tokType == tkEquals:
-      getTok(p)
-      optInd(p, a)
-      var b = a
-      a = newNodeP(nkEnumFieldDef, p)
-      addSon(a, b)
-      addSon(a, parseExpr(p))
-      if p.tok.indent < 0:
-        rawSkipComment(p, a)
-    if p.tok.tokType == tkComma:
-      getTok(p)
-      rawSkipComment(p, a)
-    addSon(result, a)
-  eat(p, tkCurlyRi)
-  if result.len <= 1:
-    lexMessageTok(p.lex, errIdentifierExpected, p.tok, prettyTok(p.tok))
-
-proc parseObjectPart(p: var TParser; needsCurly: bool): PNode
-proc parseObjectWhen(p: var TParser): PNode =
-  result = newNodeP(nkRecWhen, p)
-  while true:
-    getTok(p)                 # skip `when`, `elif`
-    var branch = newNodeP(nkElifBranch, p)
-    optInd(p, branch)
-    addSon(branch, parseExpr(p))
-    colcom(p, branch)
-    addSon(branch, parseObjectPart(p, true))
-    flexComment(p, branch)
-    addSon(result, branch)
-    if p.tok.tokType != tkElif: break
-  if p.tok.tokType == tkElse:
-    var branch = newNodeP(nkElse, p)
-    eat(p, tkElse)
-    colcom(p, branch)
-    addSon(branch, parseObjectPart(p, true))
-    flexComment(p, branch)
-    addSon(result, branch)
-
-proc parseObjectCase(p: var TParser): PNode =
-  result = newNodeP(nkRecCase, p)
-  getTokNoInd(p)
-  var a = newNodeP(nkIdentDefs, p)
-  addSon(a, identWithPragma(p))
-  eat(p, tkColon)
-  addSon(a, parseTypeDesc(p))
-  addSon(a, ast.emptyNode)
-  addSon(result, a)
-  eat(p, tkCurlyLe)
-  flexComment(p, result)
-  while true:
-    var b: PNode
-    case p.tok.tokType
-    of tkOf:
-      b = newNodeP(nkOfBranch, p)
-      exprList(p, tkColon, b)
-    of tkElse:
-      b = newNodeP(nkElse, p)
-      getTok(p)
-    else: break
-    colcom(p, b)
-    var fields = parseObjectPart(p, true)
-    if fields.kind == nkEmpty:
-      parMessage(p, errIdentifierExpected, p.tok)
-      fields = newNodeP(nkNilLit, p) # don't break further semantic checking
-    addSon(b, fields)
-    addSon(result, b)
-    if b.kind == nkElse: break
-  eat(p, tkCurlyRi)
-
-proc parseObjectPart(p: var TParser; needsCurly: bool): PNode =
-  if p.tok.tokType == tkCurlyLe:
-    result = newNodeP(nkRecList, p)
-    getTok(p)
-    rawSkipComment(p, result)
-    while true:
-      case p.tok.tokType
-      of tkCase, tkWhen, tkSymbol, tkAccent, tkNil, tkDiscard:
-        addSon(result, parseObjectPart(p, false))
-      of tkCurlyRi: break
-      else:
-        parMessage(p, errIdentifierExpected, p.tok)
-        break
-    eat(p, tkCurlyRi)
-  else:
-    if needsCurly:
-      parMessage(p, errTokenExpected, "{")
-    case p.tok.tokType
-    of tkWhen:
-      result = parseObjectWhen(p)
-    of tkCase:
-      result = parseObjectCase(p)
-    of tkSymbol, tkAccent:
-      result = parseIdentColonEquals(p, {withPragma})
-      if p.tok.indent < 0: rawSkipComment(p, result)
-    of tkNil, tkDiscard:
-      result = newNodeP(nkNilLit, p)
-      getTok(p)
-    else:
-      result = ast.emptyNode
-
-proc parseObject(p: var TParser): PNode =
-  result = newNodeP(nkObjectTy, p)
-  getTok(p)
-  if (p.tok.tokType == tkCurlyDotLe or isAt(p.tok)) and p.validInd:
-    addSon(result, parsePragma(p))
-  else:
-    addSon(result, ast.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)
-  skipComment(p, result)
-  # an initial IND{>} HAS to follow:
-  addSon(result, parseObjectPart(p, true))
-
-proc parseTypeClassParam(p: var TParser): PNode =
-  if p.tok.tokType in {tkOut, tkVar}:
-    result = newNodeP(nkVarTy, p)
-    getTok(p)
-    result.addSon(p.parseSymbol)
-  else:
-    result = p.parseSymbol
-
-proc parseTypeClass(p: var TParser): PNode =
-  #| typeClassParam = ('var' | 'out')? symbol
-  #| typeClass = typeClassParam ^* ',' (pragma)? ('of' typeDesc ^* ',')?
-  #|               &IND{>} stmt
-  result = newNodeP(nkTypeClassTy, p)
-  getTok(p)
-  var args = newNodeP(nkArgList, p)
-  addSon(result, args)
-  addSon(args, p.parseTypeClassParam)
-  while p.tok.tokType == tkComma:
-    getTok(p)
-    addSon(args, p.parseTypeClassParam)
-  if (p.tok.tokType == tkCurlyDotLe or isAt(p.tok)) and p.validInd:
-    addSon(result, parsePragma(p))
-  else:
-    addSon(result, ast.emptyNode)
-  if p.tok.tokType == tkOf and p.tok.indent < 0:
-    var a = newNodeP(nkOfInherit, p)
-    getTok(p)
-    while true:
-      addSon(a, parseTypeDesc(p))
-      if p.tok.tokType != tkComma: break
-      getTok(p)
-    addSon(result, a)
-  else:
-    addSon(result, ast.emptyNode)
-  if p.tok.tokType == tkComment:
-    skipComment(p, result)
-  addSon(result, parseCurlyStmt(p))
-
-proc parseTypeDef(p: var TParser): PNode =
-  #|
-  #| typeDef = identWithPragmaDot genericParamList? '=' optInd typeDefAux
-  #|             indAndComment?
-  result = newNodeP(nkTypeDef, p)
-  addSon(result, identWithPragma(p, allowDot=true))
-  if p.tok.tokType == tkBracketLe and p.validInd:
-    addSon(result, parseGenericParamList(p))
-  else:
-    addSon(result, ast.emptyNode)
-  if p.tok.tokType == tkEquals:
-    getTok(p)
-    optInd(p, result)
-    addSon(result, parseTypeDefAux(p))
-  else:
-    addSon(result, ast.emptyNode)
-  indAndComment(p, result)    # special extension!
-
-proc parseVarTuple(p: var TParser): PNode =
-  #| varTuple = '(' optInd identWithPragma ^+ comma optPar ')' '=' optInd expr
-  result = newNodeP(nkVarTuple, p)
-  getTok(p)                   # skip '('
-  optInd(p, result)
-  while p.tok.tokType in {tkSymbol, tkAccent}:
-    var a = identWithPragma(p)
-    addSon(result, a)
-    if p.tok.tokType != tkComma: break
-    getTok(p)
-    skipComment(p, a)
-  addSon(result, ast.emptyNode)         # no type desc
-  optPar(p)
-  eat(p, tkBracketRi)
-  eat(p, tkEquals)
-  optInd(p, result)
-  addSon(result, parseExpr(p))
-
-proc parseVariable(p: var TParser): PNode =
-  #| variable = (varTuple / identColonEquals) indAndComment
-  if p.tok.tokType == tkBracketLe: result = parseVarTuple(p)
-  else: result = parseIdentColonEquals(p, {withPragma})
-  indAndComment(p, result)
-
-proc parseBind(p: var TParser, k: TNodeKind): PNode =
-  #| bindStmt = 'bind' optInd qualifiedIdent ^+ comma
-  #| mixinStmt = 'mixin' optInd qualifiedIdent ^+ comma
-  result = newNodeP(k, p)
-  getTok(p)
-  optInd(p, result)
-  while true:
-    var a = qualifiedIdent(p)
-    addSon(result, a)
-    if p.tok.tokType != tkComma: break
-    getTok(p)
-    optInd(p, a)
-
-proc parseStmtPragma(p: var TParser): PNode =
-  result = parsePragma(p)
-  if p.tok.tokType == tkCurlyLe:
-    let a = result
-    result = newNodeI(nkPragmaBlock, a.info)
-    getTok(p)
-    skipComment(p, result)
-    result.add a
-    result.add parseStmt(p)
-    eat(p, tkCurlyRi)
-
-proc simpleStmt(p: var TParser): PNode =
-  case p.tok.tokType
-  of tkReturn: result = parseReturnOrRaise(p, nkReturnStmt)
-  of tkRaise: result = parseReturnOrRaise(p, nkRaiseStmt)
-  of tkYield: result = parseReturnOrRaise(p, nkYieldStmt)
-  of tkDiscard: result = parseReturnOrRaise(p, nkDiscardStmt)
-  of tkBreak: result = parseReturnOrRaise(p, nkBreakStmt)
-  of tkContinue: result = parseReturnOrRaise(p, nkContinueStmt)
-  of tkCurlyDotLe: result = parseStmtPragma(p)
-  of tkImport: result = parseImport(p, nkImportStmt)
-  of tkExport: result = parseImport(p, nkExportStmt)
-  of tkFrom: result = parseFromStmt(p)
-  of tkInclude: result = parseIncludeStmt(p)
-  of tkComment: result = newCommentStmt(p)
-  else:
-    if isExprStart(p): result = parseExprStmt(p)
-    else: result = ast.emptyNode
-  if result.kind notin {nkEmpty, nkCommentStmt}: skipComment(p, result)
-
-proc complexOrSimpleStmt(p: var TParser): PNode =
-  case p.tok.tokType
-  of tkIf: result = parseIfOrWhen(p, nkIfStmt)
-  of tkWhile: result = parseWhile(p)
-  of tkCase: result = parseCase(p)
-  of tkTry: result = parseTry(p)
-  of tkFor: result = parseFor(p)
-  of tkBlock: result = parseBlock(p)
-  of tkStatic: result = parseStaticOrDefer(p, nkStaticStmt)
-  of tkDefer: result = parseStaticOrDefer(p, nkDefer)
-  of tkAsm: result = parseAsm(p)
-  of tkProc: result = parseRoutine(p, nkProcDef)
-  of tkMethod: result = parseRoutine(p, nkMethodDef)
-  of tkIterator: result = parseRoutine(p, nkIteratorDef)
-  of tkMacro: result = parseRoutine(p, nkMacroDef)
-  of tkTemplate: result = parseRoutine(p, nkTemplateDef)
-  of tkConverter: result = parseRoutine(p, nkConverterDef)
-  of tkType:
-    getTok(p)
-    if p.tok.tokType == tkBracketLe:
-      getTok(p)
-      result = newNodeP(nkTypeOfExpr, p)
-      result.addSon(primary(p, pmTypeDesc))
-      eat(p, tkBracketRi)
-      result = parseOperators(p, result, -1, pmNormal)
-    else:
-      result = parseSection(p, nkTypeSection, parseTypeDef)
-  of tkConst: result = parseSection(p, nkConstSection, parseConstant)
-  of tkLet: result = parseSection(p, nkLetSection, parseVariable)
-  of tkWhen: result = parseIfOrWhen(p, nkWhenStmt)
-  of tkVar: result = parseSection(p, nkVarSection, parseVariable)
-  of tkBind: result = parseBind(p, nkBindStmt)
-  of tkMixin: result = parseBind(p, nkMixinStmt)
-  of tkUsing: result = parseSection(p, nkUsingStmt, parseVariable)
-  else: result = simpleStmt(p)
-
-proc parseStmt(p: var TParser): PNode =
-  result = complexOrSimpleStmt(p)
-
-proc parseAll*(p: var TParser): PNode =
-  ## Parses the rest of the input stream held by the parser into a PNode.
-  result = newNodeP(nkStmtList, p)
-  while p.tok.tokType != tkEof:
-    var a = complexOrSimpleStmt(p)
-    if a.kind != nkEmpty:
-      addSon(result, a)
-    else:
-      parMessage(p, errExprExpected, p.tok)
-      # bugfix: consume a token here to prevent an endless loop:
-      getTok(p)
-
-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
-  while true:
-    case p.tok.tokType
-    of tkSemiColon: getTok(p)
-    of tkEof: break
-    else:
-      result = complexOrSimpleStmt(p)
-      if result.kind == nkEmpty: parMessage(p, errExprExpected, p.tok)
-      break
diff --git a/compiler/plugins/itersgen.nim b/compiler/plugins/itersgen.nim
index dbc47e11e..ebb65dd4a 100644
--- a/compiler/plugins/itersgen.nim
+++ b/compiler/plugins/itersgen.nim
@@ -17,21 +17,21 @@ 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:
-    localError(iter.info, "first argument needs to be an iterator")
+    localError(c.config, iter.info, "first argument needs to be an iterator")
     return
   if n[2].typ.isNil:
-    localError(n[2].info, "second argument needs to be a type")
+    localError(c.config, n[2].info, "second argument needs to be a type")
     return
   if n[3].kind != nkIdent:
-    localError(n[3].info, "third argument needs to be an identifier")
+    localError(c.config, n[3].info, "third argument needs to be an identifier")
     return
 
   let t = n[2].typ.skipTypes({tyTypeDesc, tyGenericInst})
   if t.kind notin {tyRef, tyPtr} or t.lastSon.kind != tyObject:
-    localError(n[2].info,
+    localError(c.config, n[2].info,
         "type must be a non-generic ref|ptr to object with state field")
     return
-  let body = liftIterToProc(iter.sym, iter.sym.getBody, t)
+  let body = liftIterToProc(c.graph, iter.sym, iter.sym.getBody, t)
 
   let prc = newSym(skProc, n[3].ident, iter.sym.owner, iter.sym.info)
   prc.typ = copyType(iter.sym.typ, prc, false)
diff --git a/compiler/plugins/locals/locals.nim b/compiler/plugins/locals/locals.nim
index 9cbb61186..ff7f3be58 100644
--- a/compiler/plugins/locals/locals.nim
+++ b/compiler/plugins/locals/locals.nim
@@ -9,7 +9,7 @@
 
 ## 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 =
diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim
index bb7801f6f..de98a5e42 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
+  rodread, types, lookups, configuration
 
 const
   FirstCallConv* = wNimcall
@@ -44,7 +44,9 @@ const
     wWarnings, wHints,
     wLinedir, wStacktrace, wLinetrace, wOptimization, wHint, wWarning, wError,
     wFatal, wDefine, wUndef, wCompile, wLink, wLinksys, wPure, wPush, wPop,
-    wBreakpoint, wWatchPoint, wPassl, wPassc, wDeadCodeElim, wDeprecated,
+    wBreakpoint, wWatchPoint, wPassl, wPassc,
+    wDeadCodeElimUnused,  # deprecated, always on
+    wDeprecated,
     wFloatchecks, wInfChecks, wNanChecks, wPragma, wEmit, wUnroll,
     wLinearScanEnd, wPatterns, wEffects, wNoForward, wReorder, wComputedGoto,
     wInjectStmt, wDeprecated, wExperimental, wThis}
@@ -82,8 +84,12 @@ proc getPragmaVal*(procAst: PNode; name: TSpecialWord): PNode =
 proc pragma*(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords)
 # implementation
 
-proc invalidPragma*(n: PNode) =
-  localError(n.info, errInvalidPragmaX, renderTree(n, {renderNoComments}))
+const
+  errStringLiteralExpected = "string literal expected"
+  errIntLiteralExpected = "integer literal expected"
+
+proc invalidPragma*(c: PContext; n: PNode) =
+  localError(c.config, n.info, "invalid pragma: " % renderTree(n, {renderNoComments}))
 
 proc pragmaAsm*(c: PContext, n: PNode): char =
   result = '\0'
@@ -94,12 +100,12 @@ proc pragmaAsm*(c: PContext, n: PNode): char =
         case whichKeyword(it.sons[0].ident)
         of wSubsChar:
           if it.sons[1].kind == nkCharLit: result = chr(int(it.sons[1].intVal))
-          else: invalidPragma(it)
-        else: invalidPragma(it)
+          else: invalidPragma(c, it)
+        else: invalidPragma(c, it)
       else:
-        invalidPragma(it)
+        invalidPragma(c, it)
 
-proc setExternName(s: PSym, extname: string, info: TLineInfo) =
+proc setExternName(c: PContext; s: PSym, extname: string, info: TLineInfo) =
   # special cases to improve performance:
   if extname == "$1":
     s.loc.r = rope(s.name.s)
@@ -109,73 +115,73 @@ proc setExternName(s: PSym, extname: string, info: TLineInfo) =
     try:
       s.loc.r = rope(extname % s.name.s)
     except ValueError:
-      localError(info, "invalid extern name: '" & extname & "'. (Forgot to escape '$'?)")
-  if gCmd == cmdPretty and '$' notin extname:
+      localError(c.config, info, "invalid extern name: '" & extname & "'. (Forgot to escape '$'?)")
+  if c.config.cmd == cmdPretty and '$' notin extname:
     # note that '{.importc.}' is transformed into '{.importc: "$1".}'
     s.loc.flags.incl(lfFullExternalName)
 
-proc makeExternImport(s: PSym, extname: string, info: TLineInfo) =
-  setExternName(s, extname, info)
+proc makeExternImport(c: PContext; s: PSym, extname: string, info: TLineInfo) =
+  setExternName(c, s, extname, info)
   incl(s.flags, sfImportc)
   excl(s.flags, sfForward)
 
-proc makeExternExport(s: PSym, extname: string, info: TLineInfo) =
-  setExternName(s, extname, info)
+proc makeExternExport(c: PContext; s: PSym, extname: string, info: TLineInfo) =
+  setExternName(c, s, extname, info)
   incl(s.flags, sfExportc)
 
-proc processImportCompilerProc(s: PSym, extname: string, info: TLineInfo) =
-  setExternName(s, extname, info)
+proc processImportCompilerProc(c: PContext; s: PSym, extname: string, info: TLineInfo) =
+  setExternName(c, s, extname, info)
   incl(s.flags, sfImportc)
   excl(s.flags, sfForward)
   incl(s.loc.flags, lfImportCompilerProc)
 
-proc processImportCpp(s: PSym, extname: string, info: TLineInfo) =
-  setExternName(s, extname, info)
+proc processImportCpp(c: PContext; s: PSym, extname: string, info: TLineInfo) =
+  setExternName(c, s, extname, info)
   incl(s.flags, sfImportc)
   incl(s.flags, sfInfixCall)
   excl(s.flags, sfForward)
-  if gCmd == cmdCompileToC:
+  if c.config.cmd == cmdCompileToC:
     let m = s.getModule()
     incl(m.flags, sfCompileToCpp)
   extccomp.gMixedMode = true
 
-proc processImportObjC(s: PSym, extname: string, info: TLineInfo) =
-  setExternName(s, extname, info)
+proc processImportObjC(c: PContext; s: PSym, extname: string, info: TLineInfo) =
+  setExternName(c, s, extname, info)
   incl(s.flags, sfImportc)
   incl(s.flags, sfNamedParamCall)
   excl(s.flags, sfForward)
   let m = s.getModule()
   incl(m.flags, sfCompileToObjC)
 
-proc newEmptyStrNode(n: PNode): PNode {.noinline.} =
-  result = newNodeIT(nkStrLit, n.info, getSysType(tyString))
+proc newEmptyStrNode(c: PContext; n: PNode): PNode {.noinline.} =
+  result = newNodeIT(nkStrLit, n.info, getSysType(c.graph, n.info, tyString))
   result.strVal = ""
 
 proc getStrLitNode(c: PContext, n: PNode): PNode =
   if n.kind notin nkPragmaCallKinds or n.len != 2:
-    localError(n.info, errStringLiteralExpected)
+    localError(c.config, n.info, errStringLiteralExpected)
     # error correction:
-    result = newEmptyStrNode(n)
+    result = newEmptyStrNode(c, n)
   else:
     n.sons[1] = c.semConstExpr(c, n.sons[1])
     case n.sons[1].kind
     of nkStrLit, nkRStrLit, nkTripleStrLit: result = n.sons[1]
     else:
-      localError(n.info, errStringLiteralExpected)
+      localError(c.config, n.info, errStringLiteralExpected)
       # error correction:
-      result = newEmptyStrNode(n)
+      result = newEmptyStrNode(c, n)
 
 proc expectStrLit(c: PContext, n: PNode): string =
   result = getStrLitNode(c, n).strVal
 
 proc expectIntLit(c: PContext, n: PNode): int =
   if n.kind notin nkPragmaCallKinds or n.len != 2:
-    localError(n.info, errIntLiteralExpected)
+    localError(c.config, n.info, errIntLiteralExpected)
   else:
     n.sons[1] = c.semConstExpr(c, n.sons[1])
     case n.sons[1].kind
     of nkIntLit..nkInt64Lit: result = int(n.sons[1].intVal)
-    else: localError(n.info, errIntLiteralExpected)
+    else: localError(c.config, n.info, errIntLiteralExpected)
 
 proc getOptionalStr(c: PContext, n: PNode, defaultStr: string): string =
   if n.kind in nkPragmaCallKinds: result = expectStrLit(c, n)
@@ -188,7 +194,7 @@ proc processMagic(c: PContext, n: PNode, s: PSym) =
   #if sfSystemModule notin c.module.flags:
   #  liMessage(n.info, errMagicOnlyInSystem)
   if n.kind notin nkPragmaCallKinds or n.len != 2:
-    localError(n.info, errStringLiteralExpected)
+    localError(c.config, n.info, errStringLiteralExpected)
     return
   var v: string
   if n.sons[1].kind == nkIdent: v = n.sons[1].ident.s
@@ -197,7 +203,7 @@ proc processMagic(c: PContext, n: PNode, s: PSym) =
     if substr($m, 1) == v:
       s.magic = m
       break
-  if s.magic == mNone: message(n.info, warnUnknownMagic, v)
+  if s.magic == mNone: message(c.config, n.info, warnUnknownMagic, v)
 
 proc wordToCallConv(sw: TSpecialWord): TCallingConvention =
   # this assumes that the order of special words and calling conventions is
@@ -209,15 +215,11 @@ proc isTurnedOn(c: PContext, n: PNode): bool =
     let x = c.semConstBoolExpr(c, n.sons[1])
     n.sons[1] = x
     if x.kind == nkIntLit: return x.intVal != 0
-  localError(n.info, errOnOrOffExpected)
+  localError(c.config, n.info, "'on' or 'off' expected")
 
 proc onOff(c: PContext, n: PNode, op: TOptions) =
-  if isTurnedOn(c, n): gOptions = gOptions + op
-  else: gOptions = gOptions - op
-
-proc pragmaDeadCodeElim(c: PContext, n: PNode) =
-  if isTurnedOn(c, n): incl(c.module.flags, sfDeadCodeElim)
-  else: excl(c.module.flags, sfDeadCodeElim)
+  if isTurnedOn(c, n): c.config.options = c.config.options + op
+  else: c.config.options = c.config.options - op
 
 proc pragmaNoForward(c: PContext, n: PNode; flag=sfNoForward) =
   if isTurnedOn(c, n): incl(c.module.flags, flag)
@@ -229,9 +231,9 @@ proc processCallConv(c: PContext, n: PNode) =
     case sw
     of FirstCallConv..LastCallConv:
       c.optionStack[^1].defaultCC = wordToCallConv(sw)
-    else: localError(n.info, errCallConvExpected)
+    else: localError(c.config, n.info, "calling convention expected")
   else:
-    localError(n.info, errCallConvExpected)
+    localError(c.config, n.info, "calling convention expected")
 
 proc getLib(c: PContext, kind: TLibKind, path: PNode): PLib =
   for it in c.libs:
@@ -242,13 +244,13 @@ proc getLib(c: PContext, kind: TLibKind, path: PNode): PLib =
   result.path = path
   c.libs.add result
   if path.kind in {nkStrLit..nkTripleStrLit}:
-    result.isOverriden = options.isDynlibOverride(path.strVal)
+    result.isOverriden = options.isDynlibOverride(c.config, path.strVal)
 
 proc expectDynlibNode(c: PContext, n: PNode): PNode =
   if n.kind notin nkPragmaCallKinds or n.len != 2:
-    localError(n.info, errStringLiteralExpected)
+    localError(c.config, n.info, errStringLiteralExpected)
     # error correction:
-    result = newEmptyStrNode(n)
+    result = newEmptyStrNode(c, n)
   else:
     # For the OpenGL wrapper we support:
     # {.dynlib: myGetProcAddr(...).}
@@ -256,8 +258,8 @@ proc expectDynlibNode(c: PContext, n: PNode): PNode =
     if result.kind == nkSym and result.sym.kind == skConst:
       result = result.sym.ast # look it up
     if result.typ == nil or result.typ.kind notin {tyPointer, tyString, tyProc}:
-      localError(n.info, errStringLiteralExpected)
-      result = newEmptyStrNode(n)
+      localError(c.config, n.info, errStringLiteralExpected)
+      result = newEmptyStrNode(c, n)
 
 proc processDynLib(c: PContext, n: PNode, sym: PSym) =
   if (sym == nil) or (sym.kind == skModule):
@@ -280,39 +282,37 @@ proc processDynLib(c: PContext, n: PNode, sym: PSym) =
       sym.typ.callConv = ccCDecl
 
 proc processNote(c: PContext, n: PNode) =
-  if (n.kind in nkPragmaCallKinds) and (sonsLen(n) == 2) and
-      (n.sons[0].kind == nkBracketExpr) and
-      (n.sons[0].sons.len == 2) and
-      (n.sons[0].sons[1].kind == nkIdent) and
-      (n.sons[0].sons[0].kind == nkIdent):
-      #and (n.sons[1].kind == nkIdent):
+  if n.kind in nkPragmaCallKinds and len(n) == 2 and
+      n[0].kind == nkBracketExpr and
+      n[0].len == 2 and
+      n[0][1].kind == nkIdent and n[0][0].kind == nkIdent:
     var nk: TNoteKind
-    case whichKeyword(n.sons[0].sons[0].ident)
+    case whichKeyword(n[0][0].ident)
     of wHint:
-      var x = findStr(msgs.HintsToStr, n.sons[0].sons[1].ident.s)
+      var x = findStr(HintsToStr, n[0][1].ident.s)
       if x >= 0: nk = TNoteKind(x + ord(hintMin))
-      else: invalidPragma(n); return
+      else: invalidPragma(c, n); return
     of wWarning:
-      var x = findStr(msgs.WarningsToStr, n.sons[0].sons[1].ident.s)
+      var x = findStr(WarningsToStr, n[0][1].ident.s)
       if x >= 0: nk = TNoteKind(x + ord(warnMin))
-      else: invalidPragma(n); return
+      else: invalidPragma(c, n); return
     else:
-      invalidPragma(n)
+      invalidPragma(c, n)
       return
 
-    let x = c.semConstBoolExpr(c, n.sons[1])
+    let x = c.semConstBoolExpr(c, n[1])
     n.sons[1] = x
-    if x.kind == nkIntLit and x.intVal != 0: incl(gNotes, nk)
-    else: excl(gNotes, nk)
+    if x.kind == nkIntLit and x.intVal != 0: incl(c.config.notes, nk)
+    else: excl(c.config.notes, nk)
   else:
-    invalidPragma(n)
+    invalidPragma(c, n)
 
 proc processOption(c: PContext, n: PNode): bool =
   if n.kind notin nkPragmaCallKinds or n.len != 2: result = true
   elif n.sons[0].kind == nkBracketExpr: processNote(c, n)
   elif n.sons[0].kind != nkIdent: result = true
   else:
-    var sw = whichKeyword(n.sons[0].ident)
+    let sw = whichKeyword(n.sons[0].ident)
     case sw
     of wChecks: onOff(c, n, ChecksOptions)
     of wObjChecks: onOff(c, n, {optObjCheck})
@@ -339,32 +339,32 @@ proc processOption(c: PContext, n: PNode): bool =
     of wDynlib: processDynLib(c, n, nil)
     of wOptimization:
       if n.sons[1].kind != nkIdent:
-        invalidPragma(n)
+        invalidPragma(c, n)
       else:
         case n.sons[1].ident.s.normalize
         of "speed":
-          incl(gOptions, optOptimizeSpeed)
-          excl(gOptions, optOptimizeSize)
+          incl(c.config.options, optOptimizeSpeed)
+          excl(c.config.options, optOptimizeSize)
         of "size":
-          excl(gOptions, optOptimizeSpeed)
-          incl(gOptions, optOptimizeSize)
+          excl(c.config.options, optOptimizeSpeed)
+          incl(c.config.options, optOptimizeSize)
         of "none":
-          excl(gOptions, optOptimizeSpeed)
-          excl(gOptions, optOptimizeSize)
-        else: localError(n.info, errNoneSpeedOrSizeExpected)
+          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
 
 proc processPush(c: PContext, n: PNode, start: int) =
   if n.sons[start-1].kind in nkPragmaCallKinds:
-    localError(n.info, errGenerated, "'push' can't have arguments")
-  var x = newOptionEntry()
+    localError(c.config, n.info, "'push' cannot have arguments")
+  var x = newOptionEntry(c.config)
   var y = c.optionStack[^1]
-  x.options = gOptions
+  x.options = c.config.options
   x.defaultCC = y.defaultCC
   x.dynlib = y.dynlib
-  x.notes = gNotes
+  x.notes = c.config.notes
   c.optionStack.add(x)
   for i in countup(start, sonsLen(n) - 1):
     if processOption(c, n.sons[i]):
@@ -372,29 +372,29 @@ proc processPush(c: PContext, n: PNode, start: int) =
       if x.otherPragmas.isNil:
         x.otherPragmas = newNodeI(nkPragma, n.info)
       x.otherPragmas.add n.sons[i]
-    #localError(n.info, errOptionExpected)
+    #localError(c.config, n.info, errOptionExpected)
 
 proc processPop(c: PContext, n: PNode) =
   if c.optionStack.len <= 1:
-    localError(n.info, errAtPopWithoutPush)
+    localError(c.config, n.info, "{.pop.} without a corresponding {.push.}")
   else:
-    gOptions = c.optionStack[^1].options
-    gNotes = c.optionStack[^1].notes
+    c.config.options = c.optionStack[^1].options
+    c.config.notes = c.optionStack[^1].notes
     c.optionStack.setLen(c.optionStack.len - 1)
 
 proc processDefine(c: PContext, n: PNode) =
-  if (n.kind in nkPragmaCallKinds and n.len == 2) and (n.sons[1].kind == nkIdent):
-    defineSymbol(n.sons[1].ident.s)
-    message(n.info, warnDeprecated, "define")
+  if (n.kind in nkPragmaCallKinds and n.len == 2) and (n[1].kind == nkIdent):
+    defineSymbol(c.config.symbols, n[1].ident.s)
+    message(c.config, n.info, warnDeprecated, "define")
   else:
-    invalidPragma(n)
+    invalidPragma(c, n)
 
 proc processUndef(c: PContext, n: PNode) =
-  if (n.kind in nkPragmaCallKinds and n.len == 2) and (n.sons[1].kind == nkIdent):
-    undefSymbol(n.sons[1].ident.s)
-    message(n.info, warnDeprecated, "undef")
+  if (n.kind in nkPragmaCallKinds and n.len == 2) and (n[1].kind == nkIdent):
+    undefSymbol(c.config.symbols, n[1].ident.s)
+    message(c.config, n.info, warnDeprecated, "undef")
   else:
-    invalidPragma(n)
+    invalidPragma(c, n)
 
 type
   TLinkFeature = enum
@@ -408,18 +408,18 @@ proc relativeFile(c: PContext; n: PNode; ext=""): string =
   if not fileExists(result):
     if isAbsolute(s): result = s
     else:
-      result = findFile(s)
+      result = findFile(c.config, s)
       if result.len == 0: result = s
 
 proc processCompile(c: PContext, n: PNode) =
 
   proc getStrLit(c: PContext, n: PNode; i: int): string =
-    n.sons[i] = c.semConstExpr(c, n.sons[i])
-    case n.sons[i].kind
+    n.sons[i] = c.semConstExpr(c, n[i])
+    case n[i].kind
     of nkStrLit, nkRStrLit, nkTripleStrLit:
-      shallowCopy(result, n.sons[i].strVal)
+      shallowCopy(result, n[i].strVal)
     else:
-      localError(n.info, errStringLiteralExpected)
+      localError(c.config, n.info, errStringLiteralExpected)
       result = ""
 
   let it = if n.kind in nkPragmaCallKinds and n.len == 2: n.sons[1] else: n
@@ -430,26 +430,27 @@ proc processCompile(c: PContext, n: PNode) =
     for f in os.walkFiles(found):
       let nameOnly = extractFilename(f)
       var cf = Cfile(cname: f,
-          obj: completeCFilePath(dest % nameOnly),
+          obj: completeCFilePath(c.config, dest % nameOnly),
           flags: {CfileFlag.External})
-      extccomp.addExternalFileToCompile(cf)
+      extccomp.addExternalFileToCompile(c.config, cf)
   else:
     let s = expectStrLit(c, n)
     var found = parentDir(n.info.toFullPath) / s
     if not fileExists(found):
       if isAbsolute(s): found = s
       else:
-        found = findFile(s)
+        found = findFile(c.config, s)
         if found.len == 0: found = s
-    extccomp.addExternalFileToCompile(found)
+    extccomp.addExternalFileToCompile(c.config, found)
 
 proc processCommonLink(c: PContext, n: PNode, feature: TLinkFeature) =
   let found = relativeFile(c, n, CC[cCompiler].objExt)
   case feature
-  of linkNormal: extccomp.addExternalFileToLink(found)
+  of linkNormal: extccomp.addExternalFileToLink(c.config, found)
   of linkSys:
-    extccomp.addExternalFileToLink(libpath / completeCFilePath(found, false))
-  else: internalError(n.info, "processCommonLink")
+    extccomp.addExternalFileToLink(c.config,
+      c.config.libpath / completeCFilePath(c.config, found, false))
+  else: internalError(c.config, n.info, "processCommonLink")
 
 proc pragmaBreakpoint(c: PContext, n: PNode) =
   discard getOptionalStr(c, n, "")
@@ -458,7 +459,7 @@ proc pragmaWatchpoint(c: PContext, n: PNode) =
   if n.kind in nkPragmaCallKinds and n.len == 2:
     n.sons[1] = c.semExpr(c, n.sons[1])
   else:
-    invalidPragma(n)
+    invalidPragma(c, n)
 
 proc semAsmOrEmit*(con: PContext, n: PNode, marker: char): PNode =
   case n.sons[1].kind
@@ -466,7 +467,7 @@ proc semAsmOrEmit*(con: PContext, n: PNode, marker: char): PNode =
     result = newNode(if n.kind == nkAsmStmt: nkAsmStmt else: nkArgList, n.info)
     var str = n.sons[1].strVal
     if str == "":
-      localError(n.info, errEmptyAsm)
+      localError(con.config, n.info, "empty 'asm' statement")
       return
     # now parse the string literal and substitute symbols:
     var a = 0
@@ -492,12 +493,12 @@ proc semAsmOrEmit*(con: PContext, n: PNode, marker: char): PNode =
       if c < 0: break
       a = c + 1
   else:
-    illFormedAstLocal(n)
+    illFormedAstLocal(n, con.config)
     result = newNode(nkAsmStmt, n.info)
 
 proc pragmaEmit(c: PContext, n: PNode) =
   if n.kind notin nkPragmaCallKinds or n.len != 2:
-    localError(n.info, errStringLiteralExpected)
+    localError(c.config, n.info, errStringLiteralExpected)
   else:
     let n1 = n[1]
     if n1.kind == nkBracket:
@@ -511,20 +512,20 @@ proc pragmaEmit(c: PContext, n: PNode) =
       of nkStrLit, nkRStrLit, nkTripleStrLit:
         n.sons[1] = semAsmOrEmit(c, n, '`')
       else:
-        localError(n.info, errStringLiteralExpected)
+        localError(c.config, n.info, errStringLiteralExpected)
 
-proc noVal(n: PNode) =
-  if n.kind in nkPragmaCallKinds and n.len > 1: invalidPragma(n)
+proc noVal(c: PContext; n: PNode) =
+  if n.kind in nkPragmaCallKinds and n.len > 1: invalidPragma(c, n)
 
 proc pragmaUnroll(c: PContext, n: PNode) =
   if c.p.nestedLoopCounter <= 0:
-    invalidPragma(n)
+    invalidPragma(c, n)
   elif n.kind in nkPragmaCallKinds and n.len == 2:
     var unrollFactor = expectIntLit(c, n)
     if unrollFactor <% 32:
       n.sons[1] = newIntNode(nkIntLit, unrollFactor)
     else:
-      invalidPragma(n)
+      invalidPragma(c, n)
 
 proc pragmaLine(c: PContext, n: PNode) =
   if n.kind in nkPragmaCallKinds and n.len == 2:
@@ -537,26 +538,26 @@ proc pragmaLine(c: PContext, n: PNode) =
       if x.kind == nkExprColonExpr: x = x.sons[1]
       if y.kind == nkExprColonExpr: y = y.sons[1]
       if x.kind != nkStrLit:
-        localError(n.info, errStringLiteralExpected)
+        localError(c.config, n.info, errStringLiteralExpected)
       elif y.kind != nkIntLit:
-        localError(n.info, errIntLiteralExpected)
+        localError(c.config, n.info, errIntLiteralExpected)
       else:
         # XXX this produces weird paths which are not properly resolved:
-        n.info.fileIndex = msgs.fileInfoIdx(x.strVal)
-        n.info.line = int16(y.intVal)
+        n.info.fileIndex = msgs.fileInfoIdx(c.config, x.strVal)
+        n.info.line = uint16(y.intVal)
     else:
-      localError(n.info, errXExpected, "tuple")
+      localError(c.config, n.info, "tuple expected")
   else:
     # sensible default:
     n.info = getInfoContext(-1)
 
 proc processPragma(c: PContext, n: PNode, i: int) =
   let it = n[i]
-  if it.kind notin nkPragmaCallKinds and it.len == 2: invalidPragma(n)
-  elif it[0].kind != nkIdent: invalidPragma(n)
-  elif it[1].kind != nkIdent: invalidPragma(n)
+  if it.kind notin nkPragmaCallKinds and it.len == 2: invalidPragma(c, n)
+  elif it[0].kind != nkIdent: invalidPragma(c, n)
+  elif it[1].kind != nkIdent: invalidPragma(c, n)
 
-  var userPragma = newSym(skTemplate, it[1].ident, nil, it.info)
+  var userPragma = newSym(skTemplate, it[1].ident, nil, it.info, c.config.options)
   userPragma.ast = newNode(nkPragma, n.info, n.sons[i+1..^1])
   strTableAdd(c.userPragmas, userPragma)
 
@@ -564,7 +565,7 @@ proc pragmaRaisesOrTags(c: PContext, n: PNode) =
   proc processExc(c: PContext, x: PNode) =
     var t = skipTypes(c.semTypeNode(c, x, nil), skipPtrs)
     if t.kind != tyObject:
-      localError(x.info, errGenerated, "invalid type for raises/tags list")
+      localError(c.config, x.info, errGenerated, "invalid type for raises/tags list")
     x.typ = t
 
   if n.kind in nkPragmaCallKinds and n.len == 2:
@@ -574,51 +575,51 @@ proc pragmaRaisesOrTags(c: PContext, n: PNode) =
     else:
       for e in items(it): processExc(c, e)
   else:
-    invalidPragma(n)
+    invalidPragma(c, n)
 
 proc pragmaLockStmt(c: PContext; it: PNode) =
   if it.kind notin nkPragmaCallKinds or it.len != 2:
-    invalidPragma(it)
+    invalidPragma(c, it)
   else:
     let n = it[1]
     if n.kind != nkBracket:
-      localError(n.info, errGenerated, "locks pragma takes a list of expressions")
+      localError(c.config, n.info, errGenerated, "locks pragma takes a list of expressions")
     else:
       for i in 0 ..< n.len:
         n.sons[i] = c.semExpr(c, n.sons[i])
 
 proc pragmaLocks(c: PContext, it: PNode): TLockLevel =
   if it.kind notin nkPragmaCallKinds or it.len != 2:
-    invalidPragma(it)
+    invalidPragma(c, it)
   else:
     case it[1].kind
     of nkStrLit, nkRStrLit, nkTripleStrLit:
       if it[1].strVal == "unknown":
         result = UnknownLockLevel
       else:
-        localError(it[1].info, "invalid string literal for locks pragma (only allowed string is \"unknown\")")
+        localError(c.config, it[1].info, "invalid string literal for locks pragma (only allowed string is \"unknown\")")
     else:
       let x = expectIntLit(c, it)
       if x < 0 or x > MaxLockLevel:
-        localError(it[1].info, "integer must be within 0.." & $MaxLockLevel)
+        localError(c.config, it[1].info, "integer must be within 0.." & $MaxLockLevel)
       else:
         result = TLockLevel(x)
 
-proc typeBorrow(sym: PSym, n: PNode) =
+proc typeBorrow(c: PContext; sym: PSym, n: PNode) =
   if n.kind in nkPragmaCallKinds and n.len == 2:
     let it = n.sons[1]
     if it.kind != nkAccQuoted:
-      localError(n.info, "a type can only borrow `.` for now")
+      localError(c.config, n.info, "a type can only borrow `.` for now")
   incl(sym.typ.flags, tfBorrowDot)
 
-proc markCompilerProc(s: PSym) =
+proc markCompilerProc(c: PContext; s: PSym) =
   # minor hack ahead: FlowVar is the only generic .compilerProc type which
   # should not have an external name set:
   if s.kind != skType or s.name.s != "FlowVar":
-    makeExternExport(s, "$1", s.info)
+    makeExternExport(c, s, "$1", s.info)
   incl(s.flags, sfCompilerProc)
   incl(s.flags, sfUsed)
-  registerCompilerProc(s)
+  registerCompilerProc(c.graph, s)
 
 proc deprecatedStmt(c: PContext; outerPragma: PNode) =
   let pragma = outerPragma[1]
@@ -627,24 +628,24 @@ proc deprecatedStmt(c: PContext; outerPragma: PNode) =
     c.module.constraint = getStrLitNode(c, outerPragma)
     return
   if pragma.kind != nkBracket:
-    localError(pragma.info, "list of key:value pairs expected"); return
+    localError(c.config, pragma.info, "list of key:value pairs expected"); return
   for n in pragma:
     if n.kind in nkPragmaCallKinds and n.len == 2:
       let dest = qualifiedLookUp(c, n[1], {checkUndeclared})
       if dest == nil or dest.kind in routineKinds:
-        localError(n.info, warnUser, "the .deprecated pragma is unreliable for routines")
-      let src = considerQuotedIdent(n[0])
-      let alias = newSym(skAlias, src, dest, n[0].info)
+        localError(c.config, n.info, warnUser, "the .deprecated pragma is unreliable for routines")
+      let src = considerQuotedIdent(c.config, n[0])
+      let alias = newSym(skAlias, src, dest, n[0].info, c.config.options)
       incl(alias.flags, sfExported)
-      if sfCompilerProc in dest.flags: markCompilerProc(alias)
+      if sfCompilerProc in dest.flags: markCompilerProc(c, alias)
       addInterfaceDecl(c, alias)
       n.sons[1] = newSymNode(dest)
     else:
-      localError(n.info, "key:value pair expected")
+      localError(c.config, n.info, "key:value pair expected")
 
 proc pragmaGuard(c: PContext; it: PNode; kind: TSymKind): PSym =
   if it.kind notin nkPragmaCallKinds or it.len != 2:
-    invalidPragma(it); return
+    invalidPragma(c, it); return
   let n = it[1]
   if n.kind == nkSym:
     result = n.sym
@@ -656,7 +657,8 @@ 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(n), nil, n.info)
+      result = newSym(skUnknown, considerQuotedIdent(c.config, n), nil, n.info,
+        c.config.options)
   else:
     result = qualifiedLookUp(c, n, {checkUndeclared})
 
@@ -669,12 +671,12 @@ proc semCustomPragma(c: PContext, n: PNode): PNode =
   elif n.kind in nkPragmaCallKinds + {nkIdent}:
     result = n
   else:
-    invalidPragma(n)
+    invalidPragma(c, n)
     return n
 
   let r = c.semOverloadedCall(c, result, n, {skTemplate}, {})
   if r.isNil or sfCustomPragma notin r[0].sym.flags:
-    invalidPragma(n)
+    invalidPragma(c, n)
   else:
     result = r
     if n.kind == nkIdent:
@@ -682,6 +684,22 @@ proc semCustomPragma(c: PContext, n: PNode): PNode =
     elif n.kind == nkExprColonExpr:
       result.kind = n.kind # pragma(arg) -> pragma: arg
 
+proc processExperimental(c: PContext; n: PNode; s: PSym) =
+  if not isTopLevel(c):
+    localError(c.config, n.info, "'experimental' pragma only valid as toplevel statement")
+  if n.kind notin nkPragmaCallKinds or n.len != 2:
+    c.features.incl oldExperimentalFeatures
+  else:
+    n[1] = c.semConstExpr(c, n[1])
+    case n[1].kind
+    of nkStrLit, nkRStrLit, nkTripleStrLit:
+      try:
+        c.features.incl parseEnum[Feature](n[1].strVal)
+      except ValueError:
+        localError(c.config, n[1].info, "unknown experimental feature")
+    else:
+      localError(c.config, n.info, errStringLiteralExpected)
+
 proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
                   validPragmas: TSpecialWords): bool =
   var it = n.sons[i]
@@ -692,13 +710,13 @@ 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(key)
+  let ident = considerQuotedIdent(c.config, key)
   var userPragma = strTableGet(c.userPragmas, ident)
   if userPragma != nil:
     # number of pragmas increase/decrease with user pragma expansion
     inc c.instCounter
     if c.instCounter > 100:
-      globalError(it.info, errRecursiveDependencyX, userPragma.name.s)
+      globalError(c.config, it.info, "recursive dependency: " & userPragma.name.s)
 
     pragma(c, sym, userPragma.ast, validPragmas)
     n.sons[i..i] = userPragma.ast.sons # expand user pragma with its content
@@ -709,78 +727,78 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
     if k in validPragmas:
       case k
       of wExportc:
-        makeExternExport(sym, getOptionalStr(c, it, "$1"), it.info)
+        makeExternExport(c, sym, getOptionalStr(c, it, "$1"), it.info)
         incl(sym.flags, sfUsed) # avoid wrong hints
       of wImportc:
         let name = getOptionalStr(c, it, "$1")
-        cppDefine(c.graph.config, name)
-        makeExternImport(sym, name, it.info)
+        cppDefine(c.config, name)
+        makeExternImport(c, sym, name, it.info)
       of wImportCompilerProc:
         let name = getOptionalStr(c, it, "$1")
-        cppDefine(c.graph.config, name)
-        processImportCompilerProc(sym, name, it.info)
-      of wExtern: setExternName(sym, expectStrLit(c, it), it.info)
+        cppDefine(c.config, name)
+        processImportCompilerProc(c, sym, name, it.info)
+      of wExtern: setExternName(c, sym, expectStrLit(c, it), it.info)
       of wImmediate:
         if sym.kind in {skTemplate, skMacro}:
           incl(sym.flags, sfImmediate)
           incl(sym.flags, sfAllUntyped)
-          message(n.info, warnDeprecated, "use 'untyped' parameters instead; immediate")
-        else: invalidPragma(it)
+          message(c.config, n.info, warnDeprecated, "use 'untyped' parameters instead; immediate")
+        else: invalidPragma(c, it)
       of wDirty:
         if sym.kind == skTemplate: incl(sym.flags, sfDirty)
-        else: invalidPragma(it)
+        else: invalidPragma(c, it)
       of wImportCpp:
-        processImportCpp(sym, getOptionalStr(c, it, "$1"), it.info)
+        processImportCpp(c, sym, getOptionalStr(c, it, "$1"), it.info)
       of wImportObjC:
-        processImportObjC(sym, getOptionalStr(c, it, "$1"), it.info)
+        processImportObjC(c, sym, getOptionalStr(c, it, "$1"), it.info)
       of wAlign:
-        if sym.typ == nil: invalidPragma(it)
+        if sym.typ == nil: invalidPragma(c, it)
         var align = expectIntLit(c, it)
         if (not isPowerOfTwo(align) and align != 0) or align >% high(int16):
-          localError(it.info, errPowerOfTwoExpected)
+          localError(c.config, it.info, "power of two expected")
         else:
           sym.typ.align = align.int16
       of wSize:
-        if sym.typ == nil: invalidPragma(it)
+        if sym.typ == nil: invalidPragma(c, it)
         var size = expectIntLit(c, it)
         if not isPowerOfTwo(size) or size <= 0 or size > 8:
-          localError(it.info, errPowerOfTwoExpected)
+          localError(c.config, it.info, "power of two expected")
         else:
           sym.typ.size = size
       of wNodecl:
-        noVal(it)
+        noVal(c, it)
         incl(sym.loc.flags, lfNoDecl)
       of wPure, wAsmNoStackFrame:
-        noVal(it)
+        noVal(c, it)
         if sym != nil:
-          if k == wPure and sym.kind in routineKinds: invalidPragma(it)
+          if k == wPure and sym.kind in routineKinds: invalidPragma(c, it)
           else: incl(sym.flags, sfPure)
       of wVolatile:
-        noVal(it)
+        noVal(c, it)
         incl(sym.flags, sfVolatile)
       of wRegister:
-        noVal(it)
+        noVal(c, it)
         incl(sym.flags, sfRegister)
       of wThreadVar:
-        noVal(it)
-        incl(sym.flags, sfThread)
-      of wDeadCodeElim: pragmaDeadCodeElim(c, it)
+        noVal(c, it)
+        incl(sym.flags, {sfThread, sfGlobal})
+      of wDeadCodeElimUnused: discard  # deprecated, dead code elim always on
       of wNoForward: pragmaNoForward(c, it)
       of wReorder: pragmaNoForward(c, it, sfReorder)
       of wMagic: processMagic(c, it, sym)
       of wCompileTime:
-        noVal(it)
+        noVal(c, it)
         incl(sym.flags, sfCompileTime)
         incl(sym.loc.flags, lfNoDecl)
       of wGlobal:
-        noVal(it)
+        noVal(c, it)
         incl(sym.flags, sfGlobal)
         incl(sym.flags, sfPure)
       of wMerge:
         # only supported for backwards compat, doesn't do anything anymore
-        noVal(it)
+        noVal(c, it)
       of wConstructor:
-        noVal(it)
+        noVal(c, it)
         incl(sym.flags, sfConstructor)
       of wHeader:
         var lib = getLib(c, libHeader, getStrLitNode(c, it))
@@ -793,25 +811,26 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
       of wOverride:
         sym.flags.incl sfOverriden
       of wNosideeffect:
-        noVal(it)
+        noVal(c, it)
         incl(sym.flags, sfNoSideEffect)
         if sym.typ != nil: incl(sym.typ.flags, tfNoSideEffect)
       of wSideeffect:
-        noVal(it)
+        noVal(c, it)
         incl(sym.flags, sfSideEffect)
       of wNoreturn:
-        noVal(it)
+        noVal(c, it)
         incl(sym.flags, sfNoReturn)
         if sym.typ[0] != nil:
-          localError(sym.ast[paramsPos][0].info, errNoReturnWithReturnTypeNotAllowed)
+          localError(c.config, sym.ast[paramsPos][0].info,
+            ".noreturn with return type not allowed")
       of wDynlib:
         processDynLib(c, it, sym)
       of wCompilerProc, wCore:
-        noVal(it)           # compilerproc may not get a string!
+        noVal(c, it)           # compilerproc may not get a string!
         cppDefine(c.graph.config, sym.name.s)
-        if sfFromGeneric notin sym.flags: markCompilerProc(sym)
+        if sfFromGeneric notin sym.flags: markCompilerProc(c, sym)
       of wProcVar:
-        noVal(it)
+        noVal(c, it)
         incl(sym.flags, sfProcvar)
       of wExplain:
         sym.flags.incl sfExplain
@@ -823,74 +842,74 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
         elif sym != nil: incl(sym.flags, sfDeprecated)
         else: incl(c.module.flags, sfDeprecated)
       of wVarargs:
-        noVal(it)
-        if sym.typ == nil: invalidPragma(it)
+        noVal(c, it)
+        if sym.typ == nil: invalidPragma(c, it)
         else: incl(sym.typ.flags, tfVarargs)
       of wBorrow:
         if sym.kind == skType:
-          typeBorrow(sym, it)
+          typeBorrow(c, sym, it)
         else:
-          noVal(it)
+          noVal(c, it)
           incl(sym.flags, sfBorrow)
       of wFinal:
-        noVal(it)
-        if sym.typ == nil: invalidPragma(it)
+        noVal(c, it)
+        if sym.typ == nil: invalidPragma(c, it)
         else: incl(sym.typ.flags, tfFinal)
       of wInheritable:
-        noVal(it)
-        if sym.typ == nil or tfFinal in sym.typ.flags: invalidPragma(it)
+        noVal(c, it)
+        if sym.typ == nil or tfFinal in sym.typ.flags: invalidPragma(c, it)
         else: incl(sym.typ.flags, tfInheritable)
       of wPackage:
-        noVal(it)
-        if sym.typ == nil: invalidPragma(it)
+        noVal(c, it)
+        if sym.typ == nil: invalidPragma(c, it)
         else: incl(sym.flags, sfForward)
       of wAcyclic:
-        noVal(it)
-        if sym.typ == nil: invalidPragma(it)
+        noVal(c, it)
+        if sym.typ == nil: invalidPragma(c, it)
         else: incl(sym.typ.flags, tfAcyclic)
       of wShallow:
-        noVal(it)
-        if sym.typ == nil: invalidPragma(it)
+        noVal(c, it)
+        if sym.typ == nil: invalidPragma(c, it)
         else: incl(sym.typ.flags, tfShallow)
       of wThread:
-        noVal(it)
+        noVal(c, it)
         incl(sym.flags, sfThread)
         incl(sym.flags, sfProcvar)
         if sym.typ != nil:
           incl(sym.typ.flags, tfThread)
           if sym.typ.callConv == ccClosure: sym.typ.callConv = ccDefault
       of wGcSafe:
-        noVal(it)
+        noVal(c, it)
         if sym != nil:
           if sym.kind != skType: incl(sym.flags, sfThread)
           if sym.typ != nil: incl(sym.typ.flags, tfGcSafe)
-          else: invalidPragma(it)
+          else: invalidPragma(c, it)
         else:
           discard "no checking if used as a code block"
       of wPacked:
-        noVal(it)
-        if sym.typ == nil: invalidPragma(it)
+        noVal(c, it)
+        if sym.typ == nil: invalidPragma(c, it)
         else: incl(sym.typ.flags, tfPacked)
-      of wHint: message(it.info, hintUser, expectStrLit(c, it))
-      of wWarning: message(it.info, warnUser, expectStrLit(c, it))
+      of wHint: message(c.config, it.info, hintUser, expectStrLit(c, it))
+      of wWarning: message(c.config, it.info, warnUser, expectStrLit(c, it))
       of wError:
         if sym != nil and sym.isRoutine:
           # This is subtle but correct: the error *statement* is only
           # allowed for top level statements. Seems to be easier than
           # distinguishing properly between
           # ``proc p() {.error}`` and ``proc p() = {.error: "msg".}``
-          noVal(it)
+          noVal(c, it)
           incl(sym.flags, sfError)
         else:
-          localError(it.info, errUser, expectStrLit(c, it))
-      of wFatal: fatal(it.info, errUser, expectStrLit(c, it))
+          localError(c.config, it.info, errUser, expectStrLit(c, it))
+      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(expectStrLit(c, it))
-      of wPassc: extccomp.addCompileOption(expectStrLit(c, it))
+      of wPassl: extccomp.addLinkOption(c.config, expectStrLit(c, it))
+      of wPassc: extccomp.addCompileOption(c.config, expectStrLit(c, it))
       of wBreakpoint: pragmaBreakpoint(c, it)
       of wWatchPoint: pragmaWatchpoint(c, it)
       of wPush:
@@ -904,10 +923,10 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
           processPragma(c, n, i)
           result = true
       of wDiscardable:
-        noVal(it)
+        noVal(c, it)
         if sym != nil: incl(sym.flags, sfDiscardable)
       of wNoInit:
-        noVal(it)
+        noVal(c, it)
         if sym != nil: incl(sym.flags, sfNoInit)
       of wCodegenDecl: processCodegenDecl(c, it, sym)
       of wChecks, wObjChecks, wFieldChecks, wRangechecks, wBoundchecks,
@@ -918,114 +937,106 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
          wPatterns:
         if processOption(c, it):
           # calling conventions (boring...):
-          localError(it.info, errOptionExpected)
+          localError(c.config, it.info, "option expected")
       of FirstCallConv..LastCallConv:
         assert(sym != nil)
-        if sym.typ == nil: invalidPragma(it)
+        if sym.typ == nil: invalidPragma(c, it)
         else: sym.typ.callConv = wordToCallConv(k)
       of wEmit: pragmaEmit(c, it)
       of wUnroll: pragmaUnroll(c, it)
-      of wLinearScanEnd, wComputedGoto: noVal(it)
+      of wLinearScanEnd, wComputedGoto: noVal(c, it)
       of wEffects:
         # is later processed in effect analysis:
-        noVal(it)
+        noVal(c, it)
       of wIncompleteStruct:
-        noVal(it)
-        if sym.typ == nil: invalidPragma(it)
+        noVal(c, it)
+        if sym.typ == nil: invalidPragma(c, it)
         else: incl(sym.typ.flags, tfIncompleteStruct)
       of wUnchecked:
-        noVal(it)
-        if sym.typ == nil: invalidPragma(it)
+        noVal(c, it)
+        if sym.typ == nil: invalidPragma(c, it)
         else: incl(sym.typ.flags, tfUncheckedArray)
       of wUnion:
-        noVal(it)
-        if sym.typ == nil: invalidPragma(it)
+        noVal(c, it)
+        if sym.typ == nil: invalidPragma(c, it)
         else: incl(sym.typ.flags, tfUnion)
       of wRequiresInit:
-        noVal(it)
-        if sym.typ == nil: invalidPragma(it)
+        noVal(c, it)
+        if sym.typ == nil: invalidPragma(c, it)
         else: incl(sym.typ.flags, tfNeedsInit)
       of wByRef:
-        noVal(it)
+        noVal(c, it)
         if sym == nil or sym.typ == nil:
-          if processOption(c, it): localError(it.info, errOptionExpected)
+          if processOption(c, it): localError(c.config, it.info, "option expected")
         else:
           incl(sym.typ.flags, tfByRef)
       of wByCopy:
-        noVal(it)
-        if sym.kind != skType or sym.typ == nil: invalidPragma(it)
+        noVal(c, it)
+        if sym.kind != skType or sym.typ == nil: invalidPragma(c, it)
         else: incl(sym.typ.flags, tfByCopy)
       of wPartial:
-        noVal(it)
-        if sym.kind != skType or sym.typ == nil: invalidPragma(it)
+        noVal(c, it)
+        if sym.kind != skType or sym.typ == nil: invalidPragma(c, it)
         else:
           incl(sym.typ.flags, tfPartial)
-          # .partial types can only work with dead code elimination
-          # to prevent the codegen from doing anything before we compiled
-          # the whole program:
-          incl gGlobalOptions, optDeadCodeElim
       of wInject, wGensym:
         # We check for errors, but do nothing with these pragmas otherwise
         # as they are handled directly in 'evalTemplate'.
-        noVal(it)
-        if sym == nil: invalidPragma(it)
+        noVal(c, it)
+        if sym == nil: invalidPragma(c, it)
       of wLine: pragmaLine(c, it)
       of wRaises, wTags: pragmaRaisesOrTags(c, it)
       of wLocks:
         if sym == nil: pragmaLockStmt(c, it)
-        elif sym.typ == nil: invalidPragma(it)
+        elif sym.typ == nil: invalidPragma(c, it)
         else: sym.typ.lockLevel = pragmaLocks(c, it)
       of wBitsize:
         if sym == nil or sym.kind != skField:
-          invalidPragma(it)
+          invalidPragma(c, it)
         else:
           sym.bitsize = expectIntLit(c, it)
       of wGuard:
         if sym == nil or sym.kind notin {skVar, skLet, skField}:
-          invalidPragma(it)
+          invalidPragma(c, it)
         else:
           sym.guard = pragmaGuard(c, it, sym.kind)
       of wGoto:
         if sym == nil or sym.kind notin {skVar, skLet}:
-          invalidPragma(it)
+          invalidPragma(c, it)
         else:
           sym.flags.incl sfGoto
       of wExportNims:
-        if sym == nil: invalidPragma(it)
-        else: magicsys.registerNimScriptSymbol(sym)
+        if sym == nil: invalidPragma(c, it)
+        else: magicsys.registerNimScriptSymbol(c.graph, sym)
       of wInjectStmt:
         if it.kind notin nkPragmaCallKinds or it.len != 2:
-          localError(it.info, errExprExpected)
+          localError(c.config, it.info, "expression expected")
         else:
           it.sons[1] = c.semExpr(c, it.sons[1])
       of wExperimental:
-        noVal(it)
-        if isTopLevel(c):
-          c.module.flags.incl sfExperimental
-        else:
-          localError(it.info, "'experimental' pragma only valid as toplevel statement")
+        processExperimental(c, it, sym)
       of wThis:
         if it.kind in nkPragmaCallKinds and it.len == 2:
-          c.selfName = considerQuotedIdent(it[1])
+          c.selfName = considerQuotedIdent(c.config, it[1])
         elif it.kind == nkIdent or it.len == 1:
           c.selfName = getIdent("self")
         else:
-          localError(it.info, "'this' pragma is allowed to have zero or one arguments")
+          localError(c.config, it.info, "'this' pragma is allowed to have zero or one arguments")
       of wNoRewrite:
-        noVal(it)
+        noVal(c, it)
       of wBase:
-        noVal(it)
+        noVal(c, it)
         sym.flags.incl sfBase
       of wIntDefine:
         sym.magic = mIntDefine
       of wStrDefine:
         sym.magic = mStrDefine
       of wUsed:
-        noVal(it)
-        if sym == nil: invalidPragma(it)
+        noVal(c, it)
+        if sym == nil: invalidPragma(c, it)
         else: sym.flags.incl sfUsed
       of wLiftLocals: discard
-      else: invalidPragma(it)
+      else: invalidPragma(c, it)
     else:
       n.sons[i] = semCustomPragma(c, it)
 
@@ -1040,12 +1051,12 @@ proc implicitPragmas*(c: PContext, sym: PSym, n: PNode,
         var i = 0
         while i < o.len():
           if singlePragma(c, sym, o, i, validPragmas):
-            internalError(n.info, "implicitPragmas")
+            internalError(c.config, n.info, "implicitPragmas")
           inc i
         popInfoContext()
 
     if lfExportLib in sym.loc.flags and sfExportc notin sym.flags:
-      localError(n.info, errDynlibRequiresExportc)
+      localError(c.config, n.info, ".dynlib requires .exportc")
     var lib = c.optionStack[^1].dynlib
     if {lfDynamicLib, lfHeader} * sym.loc.flags == {} and
         sfImportc in sym.flags and lib != nil:
@@ -1057,7 +1068,7 @@ proc hasPragma*(n: PNode, pragma: TSpecialWord): bool =
   if n == nil or n.sons == nil:
     return false
 
-  for p in n.sons:
+  for p in n:
     var key = if p.kind in nkPragmaCallKinds and p.len > 1: p[0] else: p
     if key.kind == nkIdent and whichKeyword(key.ident) == pragma:
       return true
diff --git a/compiler/procfind.nim b/compiler/procfind.nim
index 137765ddb..042947e72 100644
--- a/compiler/procfind.nim
+++ b/compiler/procfind.nim
@@ -14,14 +14,12 @@ import
   ast, astalgo, msgs, semdata, types, trees, strutils
 
 proc equalGenericParams(procA, procB: PNode): bool =
-  if sonsLen(procA) != sonsLen(procB): return
+  if sonsLen(procA) != sonsLen(procB): return false
   for i in countup(0, sonsLen(procA) - 1):
     if procA.sons[i].kind != nkSym:
-      internalError(procA.info, "equalGenericParams")
-      return
+      return false
     if procB.sons[i].kind != nkSym:
-      internalError(procB.info, "equalGenericParams")
-      return
+      return false
     let a = procA.sons[i].sym
     let b = procB.sons[i].sym
     if a.name.id != b.name.id or
@@ -57,7 +55,7 @@ proc searchForProcOld*(c: PContext, scope: PScope, fn: PSym): PSym =
         of paramsEqual:
           return
         of paramsIncompatible:
-          localError(fn.info, errNotOverloadable, fn.name.s)
+          localError(c.config, fn.info, "overloaded '$1' leads to ambiguous calls" % fn.name.s)
           return
         of paramsNotEqual:
           discard
@@ -66,30 +64,25 @@ proc searchForProcOld*(c: PContext, scope: PScope, fn: PSym): PSym =
 proc searchForProcNew(c: PContext, scope: PScope, fn: PSym): PSym =
   const flags = {ExactGenericParams, ExactTypeDescValues,
                  ExactConstraints, IgnoreCC}
-
   var it: TIdentIter
-
   result = initIdentIter(it, scope.symbols, fn.name)
   while result != nil:
-    if result.kind == fn.kind and sameType(result.typ, fn.typ, flags):
+    if result.kind == fn.kind: #and sameType(result.typ, fn.typ, flags):
       case equalParams(result.typ.n, fn.typ.n)
       of paramsEqual:
         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]
-          localError(fn.info, errGenerated, message)
+          localError(c.config, fn.info, message)
         return
       of paramsIncompatible:
-        localError(fn.info, errNotOverloadable, fn.name.s)
+        localError(c.config, fn.info, "overloaded '$1' leads to ambiguous calls" % fn.name.s)
         return
       of paramsNotEqual:
         discard
-
     result = nextIdentIter(it, scope.symbols)
 
-  return nil
-
 proc searchForProc*(c: PContext, scope: PScope, fn: PSym): PSym =
   result = searchForProcNew(c, scope, fn)
   when false:
diff --git a/compiler/renderer.nim b/compiler/renderer.nim
index 7d513afb1..966bed769 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
+  lexer, options, idents, strutils, ast, msgs, configuration
 
 type
   TRenderFlag* = enum
@@ -39,8 +39,8 @@ type
     inPragma: int
     when defined(nimpretty):
       pendingNewlineCount: int
-      origContent: string
-
+    fid*: FileIndex
+    config*: ConfigRef
 
 # We render the source code in a two phases: The first
 # determines how long the subtree will likely be, the second
@@ -91,7 +91,7 @@ const
   MaxLineLen = 80
   LineCommentColumn = 30
 
-proc initSrcGen(g: var TSrcGen, renderFlags: TRenderFlags) =
+proc initSrcGen(g: var TSrcGen, renderFlags: TRenderFlags; config: ConfigRef) =
   g.comStack = @[]
   g.tokens = @[]
   g.indent = 0
@@ -103,6 +103,7 @@ proc initSrcGen(g: var TSrcGen, renderFlags: TRenderFlags) =
   g.pendingNL = -1
   g.pendingWhitespace = -1
   g.inGenericParams = false
+  g.config = config
 
 proc addTok(g: var TSrcGen, kind: TTokType, s: string) =
   var length = len(g.tokens)
@@ -173,39 +174,14 @@ proc put(g: var TSrcGen, kind: TTokType, s: string) =
   else:
     g.pendingWhitespace = s.len
 
-proc addNimChar(dst: var string; c: char): void =
-  case c
-  of '\0': dst.add "\\x00" # not "\\0" to avoid ambiguous cases like "\\012".
-  of '\a': dst.add "\\a" # \x07
-  of '\b': dst.add "\\b" # \x08
-  of '\t': dst.add "\\t" # \x09
-  of '\L': dst.add "\\L" # \x0A
-  of '\v': dst.add "\\v" # \x0B
-  of '\f': dst.add "\\f" # \x0C
-  of '\c': dst.add "\\c" # \x0D
-  of '\e': dst.add "\\e" # \x1B
-  of '\x01'..'\x06', '\x0E'..'\x1A', '\x1C'..'\x1F', '\x80'..'\xFF':
-    dst.add "\\x"
-    dst.add strutils.toHex(ord(c), 2)
-  of '\'', '\"', '\\':
-    dst.add '\\'
-    dst.add c
-  else:
-    dst.add c
-
-proc makeNimString(s: string): string =
-  result = "\""
-  for c in s:
-    result.addNimChar c
-  add(result, '\"')
-
 proc putComment(g: var TSrcGen, s: string) =
   if s.isNil: return
   var i = 0
+  let hi = len(s) - 1
   var isCode = (len(s) >= 2) and (s[1] != ' ')
   var ind = g.lineLen
   var com = "## "
-  while true:
+  while i <= hi:
     case s[i]
     of '\0':
       break
@@ -228,12 +204,12 @@ proc putComment(g: var TSrcGen, s: string) =
       # gets too long:
       # compute length of the following word:
       var j = i
-      while s[j] > ' ': inc(j)
+      while j <= hi and s[j] > ' ': inc(j)
       if not isCode and (g.lineLen + (j - i) > MaxLineLen):
         put(g, tkComment, com)
         optNL(g, ind)
         com = "## "
-      while s[i] > ' ':
+      while i <= hi and s[i] > ' ':
         add(com, s[i])
         inc(i)
   put(g, tkComment, com)
@@ -242,8 +218,9 @@ proc putComment(g: var TSrcGen, s: string) =
 proc maxLineLength(s: string): int =
   if s.isNil: return 0
   var i = 0
+  let hi = len(s) - 1
   var lineLen = 0
-  while true:
+  while i <= hi:
     case s[i]
     of '\0':
       break
@@ -262,7 +239,7 @@ proc maxLineLength(s: string): int =
 
 proc putRawStr(g: var TSrcGen, kind: TTokType, s: string) =
   var i = 0
-  var hi = len(s) - 1
+  let hi = len(s) - 1
   var str = ""
   while i <= hi:
     case s[i]
@@ -354,22 +331,25 @@ proc ulitAux(g: TSrcGen; n: PNode, x: BiggestInt, size: int): string =
 proc atom(g: TSrcGen; n: PNode): string =
   when defined(nimpretty):
     let comment = if n.info.commentOffsetA < n.info.commentOffsetB:
-                    " " & substr(g.origContent, n.info.commentOffsetA, n.info.commentOffsetB)
+                    " " & fileSection(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 substr(g.origContent, n.info.offsetA, n.info.offsetB) & comment
+      return fileSection(g.fid, n.info.offsetA, n.info.offsetB) & comment
   var f: float32
   case n.kind
   of nkEmpty: result = ""
   of nkIdent: result = n.ident.s
   of nkSym: result = n.sym.name.s
-  of nkStrLit: result = makeNimString(n.strVal)
-  of nkRStrLit: result = "r\"" & replace(n.strVal, "\"", "\"\"")  & '\"'
+  of nkStrLit: result = ""; result.addQuoted(n.strVal)
+  of nkRStrLit: result = "r\"" & replace(n.strVal, "\"", "\"\"") & '\"'
   of nkTripleStrLit: result = "\"\"\"" & n.strVal & "\"\"\""
-  of nkCharLit: result = "\'"; result.addNimChar chr(int(n.intVal)); result.add '\''
+  of nkCharLit:
+    result = "\'"
+    result.addEscapedChar(chr(int(n.intVal)));
+    result.add '\''
   of nkIntLit: result = litAux(g, n, n.intVal, 4)
   of nkInt8Lit: result = litAux(g, n, n.intVal, 1) & "\'i8"
   of nkInt16Lit: result = litAux(g, n, n.intVal, 2) & "\'i16"
@@ -399,7 +379,7 @@ proc atom(g: TSrcGen; n: PNode): string =
     if (n.typ != nil) and (n.typ.sym != nil): result = n.typ.sym.name.s
     else: result = "[type node]"
   else:
-    internalError("rnimsyn.atom " & $n.kind)
+    internalError(g.config, "rnimsyn.atom " & $n.kind)
     result = ""
 
 proc lcomma(g: TSrcGen; n: PNode, start: int = 0, theEnd: int = - 1): int =
@@ -1088,6 +1068,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
     if n.len > 1:
       let opr = if n[0].kind == nkIdent: n[0].ident
                 elif n[0].kind == nkSym: n[0].sym.name
+                elif n[0].kind in {nkOpenSymChoice, nkClosedSymChoice}: n[0][0].sym.name
                 else: nil
       if n[1].kind == nkPrefix or (opr != nil and renderer.isKeyword(opr)):
         put(g, tkSpaces, Space)
@@ -1443,11 +1424,11 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
     gTypeClassTy(g, n)
   else:
     #nkNone, nkExplicitTypeListCall:
-    internalError(n.info, "rnimsyn.gsub(" & $n.kind & ')')
+    internalError(g.config, n.info, "rnimsyn.gsub(" & $n.kind & ')')
 
 proc renderTree*(n: PNode, renderFlags: TRenderFlags = {}): string =
   var g: TSrcGen
-  initSrcGen(g, renderFlags)
+  initSrcGen(g, renderFlags, newPartialConfigRef())
   # do not indent the initial statement list so that
   # writeFile("file.nim", repr n)
   # produces working Nim code:
@@ -1460,17 +1441,13 @@ proc renderTree*(n: PNode, renderFlags: TRenderFlags = {}): string =
 proc `$`*(n: PNode): string = n.renderTree
 
 proc renderModule*(n: PNode, infile, outfile: string,
-                   renderFlags: TRenderFlags = {}) =
+                   renderFlags: TRenderFlags = {};
+                   fid = FileIndex(-1)) =
   var
     f: File
     g: TSrcGen
-  initSrcGen(g, renderFlags)
-  when defined(nimpretty):
-    try:
-      g.origContent = readFile(infile)
-    except IOError:
-      rawMessage(errCannotOpenFile, infile)
-
+  initSrcGen(g, renderFlags, newPartialConfigRef())
+  g.fid = fid
   for i in countup(0, sonsLen(n) - 1):
     gsub(g, n.sons[i])
     optNL(g)
@@ -1479,16 +1456,14 @@ proc renderModule*(n: PNode, infile, outfile: string,
        nkCommentStmt: putNL(g)
     else: discard
   gcoms(g)
-  if optStdout in gGlobalOptions:
-    write(stdout, g.buf)
-  elif open(f, outfile, fmWrite):
+  if open(f, outfile, fmWrite):
     write(f, g.buf)
     close(f)
   else:
-    rawMessage(errCannotOpenFile, outfile)
+    rawMessage(g.config, errGenerated, "cannot open file: " & outfile)
 
 proc initTokRender*(r: var TSrcGen, n: PNode, renderFlags: TRenderFlags = {}) =
-  initSrcGen(r, renderFlags)
+  initSrcGen(r, renderFlags, newPartialConfigRef())
   gsub(r, n)
 
 proc getNextTok*(r: var TSrcGen, kind: var TTokType, literal: var string) =
diff --git a/compiler/reorder.nim b/compiler/reorder.nim
index cde5b3a9f..d50be1d99 100644
--- a/compiler/reorder.nim
+++ b/compiler/reorder.nim
@@ -1,7 +1,8 @@
 
-import 
-  intsets, ast, idents, algorithm, renderer, parser, ospaths, strutils, 
-  sequtils, msgs, modulegraphs, syntaxes, options, modulepaths, tables
+import
+  intsets, ast, idents, algorithm, renderer, parser, ospaths, strutils,
+  sequtils, msgs, modulegraphs, syntaxes, options, modulepaths, tables,
+  configuration
 
 type
   DepN = ref object
@@ -135,13 +136,13 @@ proc hasIncludes(n:PNode): bool =
     if a.kind == nkIncludeStmt:
       return true
 
-proc includeModule*(graph: ModuleGraph; s: PSym, fileIdx: int32;
+proc includeModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex;
                     cache: IdentCache): PNode {.procvar.} =
-  result = syntaxes.parseFile(fileIdx, cache)
+  result = syntaxes.parseFile(fileIdx, cache, graph.config)
   graph.addDep(s, fileIdx)
-  graph.addIncludeDep(s.position.int32, fileIdx)
+  graph.addIncludeDep(FileIndex s.position, fileIdx)
 
-proc expandIncludes(graph: ModuleGraph, module: PSym, n: PNode, 
+proc expandIncludes(graph: ModuleGraph, module: PSym, n: PNode,
                     modulePath: string, includedFiles: var IntSet,
                     cache: IdentCache): PNode =
   # Parses includes and injects them in the current tree
@@ -151,15 +152,15 @@ proc expandIncludes(graph: ModuleGraph, module: PSym, n: PNode,
   for a in n:
     if a.kind == nkIncludeStmt:
       for i in 0..<a.len:
-        var f = checkModuleName(a.sons[i])
+        var f = checkModuleName(graph.config, a.sons[i])
         if f != InvalidFileIDX:
-          if containsOrIncl(includedFiles, f):
-            localError(a.info, errRecursiveDependencyX, f.toFilename)
+          if containsOrIncl(includedFiles, f.int):
+            localError(graph.config, a.info, "recursive dependency: '$1'" % f.toFilename)
           else:
             let nn = includeModule(graph, module, f, cache)
-            let nnn = expandIncludes(graph, module, nn, modulePath, 
+            let nnn = expandIncludes(graph, module, nn, modulePath,
                                       includedFiles, cache)
-            excl(includedFiles, f)
+            excl(includedFiles, f.int)
             for b in nnn:
               result.add b
     else:
@@ -189,7 +190,7 @@ proc haveSameKind(dns: seq[DepN]): bool =
     if dn.pnode.kind != kind:
       return false
 
-proc mergeSections(comps: seq[seq[DepN]], res: PNode) =
+proc mergeSections(conf: ConfigRef; comps: seq[seq[DepN]], res: PNode) =
   # Merges typeSections and ConstSections when they form
   # a strong component (ex: circular type definition)
   for c in comps:
@@ -229,7 +230,7 @@ proc mergeSections(comps: seq[seq[DepN]], res: PNode) =
                     wmsg &= "line " & $cs[^1].pnode.info.line &
                       " depends on line " & $cs[j].pnode.info.line &
                       ": " & cs[^1].expls[ci] & "\n"
-          message(cs[0].pnode.info, warnUser, wmsg)
+          message(conf, cs[0].pnode.info, warnUser, wmsg)
 
           var i = 0
           while i < cs.len:
@@ -273,9 +274,9 @@ proc hasCommand(n: PNode): bool =
   of nkStmtList, nkStmtListExpr, nkWhenStmt, nkElifBranch, nkElse,
       nkStaticStmt, nkLetSection, nkConstSection, nkVarSection,
       nkIdentDefs:
-        for a in n:
-          if a.hasCommand:
-            return true
+    for a in n:
+      if a.hasCommand:
+        return true
   else:
     return false
 
@@ -430,7 +431,7 @@ proc reorder*(graph: ModuleGraph, n: PNode, module: PSym, cache: IdentCache): PN
     return n
   var includedFiles = initIntSet()
   let mpath = module.fileIdx.toFullPath
-  let n = expandIncludes(graph, module, n, mpath, 
+  let n = expandIncludes(graph, module, n, mpath,
                           includedFiles, cache).splitSections
   result = newNodeI(nkStmtList, n.info)
   var deps = newSeq[(IntSet, IntSet)](n.len)
@@ -441,4 +442,4 @@ proc reorder*(graph: ModuleGraph, n: PNode, module: PSym, cache: IdentCache): PN
 
   var g = buildGraph(n, deps)
   let comps = getStrongComponents(g)
-  mergeSections(comps, result)
+  mergeSections(graph.config, comps, result)
diff --git a/compiler/rod.nim b/compiler/rod.nim
index bc2f3931e..c144f15ef 100644
--- a/compiler/rod.nim
+++ b/compiler/rod.nim
@@ -9,124 +9,18 @@
 
 ## This module implements the canonalization for the various caching mechanisms.
 
-import ast, idgen
+import ast, idgen, msgs
 
 when not defined(nimSymbolfiles):
   template setupModuleCache* = discard
   template storeNode*(module: PSym; n: PNode) = discard
   template loadNode*(module: PSym; index: var int): PNode = PNode(nil)
 
-  template getModuleId*(fileIdx: int32; fullpath: string): int = getID()
+  template getModuleId*(fileIdx: FileIndex; fullpath: string): int = getID()
 
-  template addModuleDep*(module, fileIdx: int32; isIncludeFile: bool) = discard
+  template addModuleDep*(module, fileIdx: FileIndex; isIncludeFile: bool) = discard
 
   template storeRemaining*(module: PSym) = discard
 
 else:
   include rodimpl
-
-when false:
-  type
-    BlobWriter* = object
-      buf: string
-      pos: int
-
-    SerializationAction = enum acRead, acWrite
-
-  # Varint implementation inspired by SQLite.
-  proc rdVaruint64(z: ptr UncheckedArray[byte]; n: int; pResult: var uint64): int =
-    if z[0] <= 240:
-      pResult = z[0]
-      return 1
-    if z[0] <= 248:
-      if n < 2: return 0
-      pResult = (z[0] - 241) * 256 + z[1] + 240
-      return 2
-    if n < z[0]-246: return 0
-    if z[0] == 249:
-      pResult = 2288 + 256*z[1] + z[2]
-      return 3
-    if z[0] == 250:
-      pResult = (z[1] shl 16u64) + (z[2] shl 8u64) + z[3]
-      return 4
-    let x = (z[1] shl 24) + (z[2] shl 16) + (z[3] shl 8) + z[4]
-    if z[0] == 251:
-      pResult = x
-      return 5
-    if z[0] == 252:
-      pResult = (((uint64)x) shl 8) + z[5]
-      return 6
-    if z[0] == 253:
-      pResult = (((uint64)x) shl 16) + (z[5] shl 8) + z[6]
-      return 7
-    if z[0] == 254:
-      pResult = (((uint64)x) shl 24) + (z[5] shl 16) + (z[6] shl 8) + z[7]
-      return 8
-    pResult = (((uint64)x) shl 32) +
-                (0xffffffff & ((z[5] shl 24) + (z[6] shl 16) + (z[7] shl 8) + z[8]))
-    return 9
-
-  proc varintWrite32(z: ptr UncheckedArray[byte]; y: uint32) =
-    z[0] = uint8(y shr 24)
-    z[1] = uint8(y shr 16)
-    z[2] = uint8(y shr 8)
-    z[3] = uint8(y)
-
-  proc sqlite4PutVarint64(z: ptr UncheckedArray[byte], x: uint64): int =
-    ## Write a varint into z. The buffer z must be at least 9 characters
-    ## long to accommodate the largest possible varint. Returns the number of
-    ## bytes used.
-    if x <= 240:
-      z[0] = uint8 x
-      return 1
-    if x <= 2287:
-      y = uint32(x - 240)
-      z[0] = uint8(y shr 8 + 241)
-      z[1] = uint8(y and 255)
-      return 2
-    if x <= 67823:
-      y = uint32(x - 2288)
-      z[0] = 249
-      z[1] = uint8(y shr 8)
-      z[2] = uint8(y and 255)
-      return 3
-    let y = uint32 x
-    let w = uint32(x shr 32)
-    if w == 0:
-      if y <= 16777215:
-        z[0] = 250
-        z[1] = uint8(y shr 16)
-        z[2] = uint8(y shr 8)
-        z[3] = uint8(y)
-        return 4
-      z[0] = 251
-      varintWrite32(z+1, y)
-      return 5
-    if w <= 255:
-      z[0] = 252
-      z[1] = uint8 w
-      varintWrite32(z+2, y)
-      return 6
-    if w <= 65535:
-      z[0] = 253
-      z[1] = uint8(w shr 8)
-      z[2] = uint8 w
-      varintWrite32(z+3, y)
-      return 7
-    if w <= 16777215:
-      z[0] = 254
-      z[1] = uint8(w shr 16)
-      z[2] = uint8(w shr 8)
-      z[3] = uint8 w
-      varintWrite32(z+4, y)
-      return 8
-    z[0] = 255
-    varintWrite32(z+1, w)
-    varintWrite32(z+5, y)
-    return 9
-
-  template field(x: BiggestInt; action: SerializationAction) =
-    when action == acRead:
-      readBiggestInt(x)
-    else:
-      writeBiggestInt()
diff --git a/compiler/rodread.nim b/compiler/rodread.nim
index 84f175cb5..52e7a924c 100644
--- a/compiler/rodread.nim
+++ b/compiler/rodread.nim
@@ -90,7 +90,8 @@
 
 import
   os, options, strutils, nversion, ast, astalgo, msgs, platform, condsyms,
-  ropes, idents, std / sha1, idgen, types, rodutils, memfiles, tables
+  ropes, idents, std / sha1, idgen, types, rodutils, memfiles, tables,
+  configuration
 
 type
   TReasonForRecompile* = enum ## all the reasons that can trigger recompilation
@@ -126,8 +127,8 @@ type
     s: cstring               # mmap'ed file contents
     options: TOptions
     reason: TReasonForRecompile
-    modDeps: seq[int32]
-    files: seq[int32]
+    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
@@ -143,6 +144,7 @@ type
     origFile: string
     inViewMode: bool
     cache*: IdentCache
+    config: ConfigRef
 
   PRodReader* = ref TRodReader
 
@@ -163,11 +165,11 @@ proc decodeLineInfo(r: PRodReader, info: var TLineInfo) =
     else: info.col = int16(decodeVInt(r.s, r.pos))
     if r.s[r.pos] == ',':
       inc(r.pos)
-      if r.s[r.pos] == ',': info.line = -1'i16
-      else: info.line = int16(decodeVInt(r.s, 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)], info.line, info.col)
+        info = newLineInfo(r.files[decodeVInt(r.s, r.pos)], int info.line, info.col)
 
 proc skipNode(r: PRodReader) =
   assert r.s[r.pos] == '('
@@ -222,14 +224,14 @@ proc decodeNodeLazyBody(r: PRodReader, fInfo: TLineInfo,
         var fl = decodeStr(r.s, r.pos)
         result.ident = r.cache.getIdent(fl)
       else:
-        internalError(result.info, "decodeNode: nkIdent")
+        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(result.info, "decodeNode: nkSym")
+        internalError(r.config, result.info, "decodeNode: nkSym")
     else:
       var i = 0
       while r.s[r.pos] != ')':
@@ -241,9 +243,9 @@ proc decodeNodeLazyBody(r: PRodReader, fInfo: TLineInfo,
           addSonNilAllowed(result, decodeNodeLazyBody(r, result.info, nil))
         inc i
     if r.s[r.pos] == ')': inc(r.pos)
-    else: internalError(result.info, "decodeNode: ')' missing")
+    else: internalError(r.config, result.info, "decodeNode: ')' missing")
   else:
-    internalError(fInfo, "decodeNode: '(' missing " & $r.pos)
+    internalError(r.config, fInfo, "decodeNode: '(' missing " & $r.pos)
 
 proc decodeNode(r: PRodReader, fInfo: TLineInfo): PNode =
   result = decodeNodeLazyBody(r, fInfo, nil)
@@ -277,7 +279,7 @@ proc decodeLoc(r: PRodReader, loc: var TLoc, info: TLineInfo) =
     else:
       loc.r = nil
     if r.s[r.pos] == '>': inc(r.pos)
-    else: internalError(info, "decodeLoc " & r.s[r.pos])
+    else: internalError(r.config, info, "decodeLoc " & r.s[r.pos])
 
 proc decodeType(r: PRodReader, info: TLineInfo): PType =
   result = nil
@@ -294,7 +296,7 @@ proc decodeType(r: PRodReader, info: TLineInfo): PType =
     setId(result.id)
     if debugIds: registerID(result)
   else:
-    internalError(info, "decodeType: no id")
+    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())
@@ -345,14 +347,14 @@ proc decodeType(r: PRodReader, info: TLineInfo): PType =
     doAssert r.s[r.pos] == '\20'
     inc(r.pos)
     let y = rrGetSym(r, decodeVInt(r.s, r.pos), info)
-    result.methods.safeAdd((x, y))
+    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(info, "decodeType ^(" & r.s[r.pos])
+      else: internalError(r.config, info, "decodeType ^(" & r.s[r.pos])
       rawAddSon(result, nil)
     else:
       var d = decodeVInt(r.s, r.pos)
@@ -364,10 +366,10 @@ proc decodeLib(r: PRodReader, info: TLineInfo): PLib =
     new(result)
     inc(r.pos)
     result.kind = TLibKind(decodeVInt(r.s, r.pos))
-    if r.s[r.pos] != '|': internalError("decodeLib: 1")
+    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("decodeLib: 2")
+    if r.s[r.pos] != '|': internalError(r.config, "decodeLib: 2")
     inc(r.pos)
     result.path = decodeNode(r, info)
 
@@ -385,7 +387,7 @@ proc decodeInstantiations(r: PRodReader; info: TLineInfo;
     if r.s[r.pos] == '\20':
       inc(r.pos)
       ii.compilesId = decodeVInt(r.s, r.pos)
-    s.safeAdd ii
+    s.add ii
 
 proc decodeSym(r: PRodReader, info: TLineInfo): PSym =
   var
@@ -403,12 +405,12 @@ proc decodeSym(r: PRodReader, info: TLineInfo): PSym =
     id = decodeVInt(r.s, r.pos)
     setId(id)
   else:
-    internalError(info, "decodeSym: no id")
+    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(info, "decodeSym: no ident")
+    internalError(r.config, info, "decodeSym: no ident")
   #echo "decoding: {", ident.s
   result = r.syms.getOrDefault(id)
   if result == nil:
@@ -417,7 +419,7 @@ proc decodeSym(r: PRodReader, info: TLineInfo): PSym =
     r.syms[result.id] = result
     if debugIds: registerID(result)
   elif result.id != id:
-    internalError(info, "decodeSym: wrong id")
+    internalError(r.config, info, "decodeSym: wrong id")
   elif result.kind != skStub and not r.inViewMode:
     # we already loaded the symbol
     return
@@ -465,7 +467,7 @@ proc decodeSym(r: PRodReader, info: TLineInfo): PSym =
   of skType, skGenericParam:
     while r.s[r.pos] == '\14':
       inc(r.pos)
-      result.typeInstCache.safeAdd rrGetType(r, decodeVInt(r.s, r.pos), result.info)
+      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':
@@ -512,7 +514,7 @@ proc skipSection(r: PRodReader) =
       else: discard
       inc(r.pos)
   else:
-    internalError("skipSection " & $r.line)
+    internalError(r.config, "skipSection " & $r.line)
 
 proc rdWord(r: PRodReader): string =
   result = ""
@@ -530,7 +532,7 @@ proc newStub(r: PRodReader, name: string, id: int): PSym =
   if debugIds: registerID(result)
 
 proc processInterf(r: PRodReader, module: PSym) =
-  if r.interfIdx == 0: internalError("processInterf")
+  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)
@@ -543,7 +545,7 @@ proc processInterf(r: PRodReader, module: PSym) =
     r.syms[s.id] = s
 
 proc processCompilerProcs(r: PRodReader, module: PSym) =
-  if r.compilerProcsIdx == 0: internalError("processCompilerProcs")
+  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)
@@ -586,7 +588,7 @@ proc cmdChangeTriggersRecompilation(old, new: TCommands): bool =
   # new command forces us to consider it here :-)
   case old
   of cmdCompileToC, cmdCompileToCpp, cmdCompileToOC,
-      cmdCompileToJS, cmdCompileToPHP, cmdCompileToLLVM:
+      cmdCompileToJS, cmdCompileToLLVM:
     if new in {cmdDoc, cmdCheck, cmdIdeTools, cmdPretty, cmdDef,
                cmdInteractive}:
       return false
@@ -621,26 +623,26 @@ proc processRodFile(r: PRodReader, hash: SecureHash) =
     of "OPTIONS":
       inc(r.pos)              # skip ':'
       r.options = cast[TOptions](int32(decodeVInt(r.s, r.pos)))
-      if options.gOptions != r.options: r.reason = rrOptions
+      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 gGlobalOptions-harmlessOptions != dep-harmlessOptions:
+      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, gCmd): r.reason = rrOptions
+      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 condsyms.isDefined(r.cache.getIdent(w)):
+        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.reason = rrDefines
+      if d != countDefinedSymbols(r.config.symbols): r.reason = rrDefines
     of "FILES":
       inc(r.pos, 2)           # skip "(\10"
       inc(r.line)
@@ -648,7 +650,7 @@ proc processRodFile(r: PRodReader, hash: SecureHash) =
         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(finalPath.fileInfoIdx)
+        r.files.add(fileInfoIdx(r.config, finalPath))
         inc(r.pos)            # skip #10
         inc(r.line)
       if r.s[r.pos] == ')': inc(r.pos)
@@ -696,7 +698,7 @@ proc processRodFile(r: PRodReader, hash: SecureHash) =
       r.initIdx = r.pos + 2   # "(\10"
       skipSection(r)
     else:
-      internalError("invalid section: '" & section &
+      internalError(r.config, "invalid section: '" & section &
                     "' at " & $r.line & " in " & r.filename)
       #MsgWriteln("skipping section: " & section &
       #           " at " & $r.line & " in " & r.filename)
@@ -712,9 +714,11 @@ proc startsWith(buf: cstring, token: string, pos = 0): bool =
   result = s == token.len
 
 proc newRodReader(modfilename: string, hash: SecureHash,
-                  readerIndex: int; cache: IdentCache): PRodReader =
+                  readerIndex: int; cache: IdentCache;
+                  config: ConfigRef): PRodReader =
   new(result)
   result.cache = cache
+  result.config = config
   try:
     result.memfile = memfiles.open(modfilename)
   except OSError:
@@ -757,13 +761,13 @@ proc rrGetType(r: PRodReader, id: int, info: TLineInfo): PType =
     # load the type:
     var oldPos = r.pos
     var d = iiTableGet(r.index.tab, id)
-    if d == InvalidKey: internalError(info, "rrGetType")
+    if d == InvalidKey: internalError(r.config, info, "rrGetType")
     r.pos = d + r.dataIdx
     result = decodeType(r, info)
     r.pos = oldPos
 
 type
-  TFileModuleRec{.final.} = object
+  TFileModuleRec = object
     filename*: string
     reason*: TReasonForRecompile
     rd*: PRodReader
@@ -776,7 +780,7 @@ var gMods*: TFileModuleMap = @[]
 
 proc decodeSymSafePos(rd: PRodReader, offset: int, info: TLineInfo): PSym =
   # all compiled modules
-  if rd.dataIdx == 0: internalError(info, "dataIdx == 0")
+  if rd.dataIdx == 0: internalError(rd.config, info, "dataIdx == 0")
   var oldPos = rd.pos
   rd.pos = offset + rd.dataIdx
   result = decodeSym(rd, info)
@@ -811,7 +815,7 @@ proc rrGetSym(r: PRodReader, id: int, info: TLineInfo): PSym =
       if moduleID < 0:
         var x = ""
         encodeVInt(id, x)
-        internalError(info, "missing from both indexes: +" & x)
+        internalError(r.config, info, "missing from both indexes: +" & x)
       var rd = getReader(moduleID)
       doAssert rd != nil
       d = iiTableGet(rd.index.tab, id)
@@ -821,14 +825,14 @@ proc rrGetSym(r: PRodReader, id: int, info: TLineInfo): PSym =
         var x = ""
         encodeVInt(id, x)
         when false: findSomeWhere(id)
-        internalError(info, "rrGetSym: no reader found: +" & x)
+        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("loadInitSection")
+  if r.initIdx == 0 or r.dataIdx == 0: internalError(r.config, "loadInitSection")
   var oldPos = r.pos
   r.pos = r.initIdx
   result = newNode(nkStmtList)
@@ -845,7 +849,7 @@ 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("importConverters")
+    internalError(r.config, "importConverters")
   r.pos = r.convertersIdx
   while r.s[r.pos] > '\x0A':
     var d = decodeVInt(r.s, r.pos)
@@ -854,36 +858,36 @@ proc loadConverters(r: PRodReader) =
 
 proc loadMethods(r: PRodReader) =
   if r.methodsIdx == 0 or r.dataIdx == 0:
-    internalError("loadMethods")
+    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: int32): SecureHash =
-  if fileIdx <% gMods.len and gMods[fileIdx].hashDone:
-    return gMods[fileIdx].hash
+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 >= gMods.len: setLen(gMods, fileIdx+1)
-  gMods[fileIdx].hash = result
+  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: int32; cache: IdentCache): TReasonForRecompile =
+proc checkDep(fileIdx: FileIndex; cache: IdentCache; conf: ConfigRef): TReasonForRecompile =
   assert fileIdx != InvalidFileIDX
-  growCache gMods, fileIdx
-  if gMods[fileIdx].reason != rrEmpty:
+  growCache gMods, fileIdx.int32
+  if gMods[fileIdx.int32].reason != rrEmpty:
     # reason has already been computed for this module:
-    return gMods[fileIdx].reason
+    return gMods[fileIdx.int32].reason
   let filename = fileIdx.toFilename
   var hash = getHash(fileIdx)
-  gMods[fileIdx].reason = rrNone  # we need to set it here to avoid cycles
+  gMods[fileIdx.int32].reason = rrNone  # we need to set it here to avoid cycles
   result = rrNone
-  var rodfile = toGeneratedFile(filename.withPackageName, RodExt)
-  var r = newRodReader(rodfile, hash, fileIdx, cache)
+  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:
@@ -894,32 +898,31 @@ proc checkDep(fileIdx: int32; cache: IdentCache): TReasonForRecompile =
       # 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)
+      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)
+        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(hintProcessing, reasonToFrmt[result] % filename)
-  if result != rrNone or optForceFullMake in gGlobalOptions:
+    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].rd = r
-  gMods[fileIdx].reason = result  # now we know better
+  gMods[fileIdx.int32].rd = r
+  gMods[fileIdx.int32].reason = result  # now we know better
 
-proc handleSymbolFile*(module: PSym; cache: IdentCache): PRodReader =
-  let fileIdx = module.fileIdx
-  if gSymbolFiles in {disabledSf, writeOnlySf, v2Sf}:
+proc handleSymbolFile*(module: PSym; cache: IdentCache; conf: ConfigRef): PRodReader =
+  if conf.symbolFiles in {disabledSf, writeOnlySf, v2Sf}:
     module.id = getID()
     return nil
-  idgen.loadMaxIds(options.gProjectPath / options.gProjectName)
-
-  discard checkDep(fileIdx, cache)
-  if gMods[fileIdx].reason == rrEmpty: internalError("handleSymbolFile")
-  result = gMods[fileIdx].rd
+  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
@@ -931,19 +934,20 @@ proc handleSymbolFile*(module: PSym; cache: IdentCache): PRodReader =
     module.id = getID()
 
 proc rawLoadStub(s: PSym) =
-  if s.kind != skStub: internalError("loadStub")
+  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")
+  #if d == InvalidKey: internalError("loadStub: invalid key")
   var rs = decodeSymSafePos(rd, d, unknownLineInfo())
-  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")
-  #MessageOut('loaded stub: ' + s.name.s);
+  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`.
@@ -1031,9 +1035,8 @@ proc writeSym(f: File; s: PSym) =
   if s.magic != mNone:
     f.write('@')
     f.write($s.magic)
-  if s.options != gOptions:
-    f.write('!')
-    f.write($s.options)
+  f.write('!')
+  f.write($s.options)
   if s.position != 0:
     f.write('%')
     f.write($s.position)
@@ -1084,9 +1087,10 @@ proc writeType(f: File; t: PType) =
   f.write("]\n")
 
 proc viewFile(rodfile: string) =
-  var r = newRodReader(rodfile, secureHash(""), 0, newIdentCache())
+  let conf = newConfigRef()
+  var r = newRodReader(rodfile, secureHash(""), 0, newIdentCache(), conf)
   if r == nil:
-    rawMessage(errGenerated, "cannot open file (or maybe wrong version):" &
+    rawMessage(conf, errGenerated, "cannot open file (or maybe wrong version):" &
        rodfile)
     return
   r.inViewMode = true
@@ -1134,9 +1138,9 @@ proc viewFile(rodfile: string) =
       outf.write("FILES(\n")
       while r.s[r.pos] != ')':
         let relativePath = decodeStr(r.s, r.pos)
-        let resolvedPath = relativePath.findModule(r.origFile)
+        let resolvedPath = findModule(conf, relativePath, r.origFile)
         let finalPath = if resolvedPath.len > 0: resolvedPath else: relativePath
-        r.files.add(finalPath.fileInfoIdx)
+        r.files.add(fileInfoIdx(conf, finalPath))
         inc(r.pos)            # skip #10
         inc(r.line)
         outf.writeLine finalPath
@@ -1153,7 +1157,7 @@ proc viewFile(rodfile: string) =
         if r.s[r.pos] == '\x0A':
           inc(r.pos)
           inc(r.line)
-        outf.write(w, " ", inclHash, "\n")
+        outf.write(w.int32, " ", inclHash, "\n")
       if r.s[r.pos] == ')': inc(r.pos)
       outf.write(")\n")
     of "DEPS":
@@ -1163,7 +1167,7 @@ proc viewFile(rodfile: string) =
         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])
+        outf.write(" ", r.files[v].int32)
       outf.write("\n")
     of "INTERF",  "COMPILERPROCS":
       inc r.pos, 2
@@ -1228,7 +1232,7 @@ proc viewFile(rodfile: string) =
       if r.s[r.pos] == ')': inc r.pos
       outf.write("<not supported by viewer>)\n")
     else:
-      internalError("invalid section: '" & section &
+      internalError(r.config, "invalid section: '" & section &
                     "' at " & $r.line & " in " & r.filename)
       skipSection(r)
     if r.s[r.pos] == '\x0A':
diff --git a/compiler/rodwrite.nim b/compiler/rodwrite.nim
index 96deb1d5a..4686baf2b 100644
--- a/compiler/rodwrite.nim
+++ b/compiler/rodwrite.nim
@@ -37,12 +37,13 @@ type
     files: TStringSeq
     origFile: string
     cache: IdentCache
+    config: ConfigRef
 
   PRodWriter = ref TRodWriter
 
-proc getDefines(): string =
+proc getDefines(conf: ConfigRef): string =
   result = ""
-  for d in definedSymbolNames():
+  for d in definedSymbolNames(conf.symbols):
     if result.len != 0: add(result, " ")
     add(result, d)
 
@@ -55,10 +56,12 @@ proc fileIdx(w: PRodWriter, filename: string): int =
   w.files[result] = filename
 
 template filename*(w: PRodWriter): string =
-  w.module.filename
+  toFilename(FileIndex w.module.position)
 
-proc newRodWriter(hash: SecureHash, module: PSym; cache: IdentCache): PRodWriter =
+proc newRodWriter(hash: SecureHash, module: PSym; cache: IdentCache;
+                  config: ConfigRef): PRodWriter =
   new(result)
+  result.config = config
   result.sstack = @[]
   result.tstack = @[]
   initIiTable(result.index.tab)
@@ -67,8 +70,8 @@ proc newRodWriter(hash: SecureHash, module: PSym; cache: IdentCache): PRodWriter
   result.imports.r = ""
   result.hash = hash
   result.module = module
-  result.defines = getDefines()
-  result.options = options.gOptions
+  result.defines = getDefines(config)
+  result.options = config.options
   result.files = @[]
   result.inclDeps = ""
   result.modDeps = ""
@@ -83,14 +86,14 @@ proc newRodWriter(hash: SecureHash, module: PSym; cache: IdentCache): PRodWriter
 
 proc addModDep(w: PRodWriter, dep: string; info: TLineInfo) =
   if w.modDeps.len != 0: add(w.modDeps, ' ')
-  let resolved = dep.findModule(info.toFullPath)
+  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 = dep.findModule(info.toFullPath)
+  let resolved = findModule(w.config, dep, info.toFullPath)
   encodeVInt(fileIdx(w, resolved), w.inclDeps)
   add(w.inclDeps, " ")
   encodeStr($secureHashFile(resolved), w.inclDeps)
@@ -125,14 +128,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(fileIdx(w, toFullPath(n.info)), 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)
@@ -201,7 +204,7 @@ proc encodeType(w: PRodWriter, t: PType, result: var string) =
     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(w.config, "encodeType: tyForward")
   # for the new rodfile viewer we use a preceding [ so that the data section
   # can easily be disambiguated:
   add(result, '[')
@@ -303,7 +306,7 @@ 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)
+  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:
@@ -454,7 +457,7 @@ proc processStacks(w: PRodWriter, finalPass: bool) =
     oldS = slen
     oldT = tlen
   if finalPass and (oldS != 0 or oldT != 0):
-    internalError("could not serialize some forwarded symbols/types")
+    internalError(w.config, "could not serialize some forwarded symbols/types")
 
 proc rawAddInterfaceSym(w: PRodWriter, s: PSym) =
   pushSym(w, s)
@@ -476,8 +479,8 @@ proc addStmt(w: PRodWriter, n: PNode) =
 proc writeRod(w: PRodWriter) =
   processStacks(w, true)
   var f: File
-  if not open(f, completeGeneratedFilePath(changeFileExt(
-                      w.filename.withPackageName, RodExt)),
+  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
@@ -506,12 +509,12 @@ proc writeRod(w: PRodWriter) =
   f.write(rodNL)
 
   var goptions = "GOPTIONS:"
-  encodeVInt(cast[int32](gGlobalOptions), goptions)
+  encodeVInt(cast[int32](w.config.globalOptions), goptions)
   f.write(goptions)
   f.write(rodNL)
 
   var cmd = "CMD:"
-  encodeVInt(cast[int32](gCmd), cmd)
+  encodeVInt(cast[int32](w.config.cmd), cmd)
   f.write(cmd)
   f.write(rodNL)
 
@@ -587,17 +590,17 @@ proc process(c: PPassContext, n: PNode): PNode =
   of nkProcDef, nkFuncDef, nkIteratorDef, nkConverterDef,
       nkTemplateDef, nkMacroDef:
     let s = n.sons[namePos].sym
-    if s == nil: internalError(n.info, "rodwrite.process")
+    if s == nil: internalError(w.config, n.info, "rodwrite.process")
     if n.sons[bodyPos] == nil:
-      internalError(n.info, "rodwrite.process: body is 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(n.info, "rodwrite.process")
+    if s == nil: internalError(w.config, n.info, "rodwrite.process")
     if n.sons[bodyPos] == nil:
-      internalError(n.info, "rodwrite.process: body is 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)
@@ -612,7 +615,7 @@ proc process(c: PPassContext, n: PNode): PNode =
     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(a.info, "rodwrite.process")
+      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
@@ -627,22 +630,22 @@ proc process(c: PPassContext, n: PNode): PNode =
       #        end
   of nkImportStmt:
     for i in countup(0, sonsLen(n) - 1):
-      addModDep(w, getModuleName(n.sons[i]), n.info)
+      addModDep(w, getModuleName(w.config, n.sons[i]), n.info)
     addStmt(w, n)
   of nkFromStmt, nkImportExceptStmt:
-    addModDep(w, getModuleName(n.sons[0]), n.info)
+    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(n.sons[i]), n.info)
+      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("rodwrite: module ID not set")
-  var w = newRodWriter(rodread.getHash module.fileIdx, module, cache)
+  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
 
@@ -650,7 +653,7 @@ proc myClose(graph: ModuleGraph; c: PPassContext, n: PNode): PNode =
   result = process(c, n)
   var w = PRodWriter(c)
   writeRod(w)
-  idgen.saveMaxIds(options.gProjectPath / options.gProjectName)
+  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 358ce8a53..05d5e840c 100644
--- a/compiler/ropes.nim
+++ b/compiler/ropes.nim
@@ -188,11 +188,11 @@ iterator leaves*(r: Rope): string =
     var stack = @[r]
     while stack.len > 0:
       var it = stack.pop
-      while isNil(it.data):
+      while it.left != nil:
+        assert it.right != nil
         stack.add(it.right)
         it = it.left
         assert(it != nil)
-      assert(it.data != nil)
       yield it.data
 
 iterator items*(r: Rope): char =
@@ -251,7 +251,7 @@ proc `%`*(frmt: FormatStr, args: openArray[Rope]): Rope =
         while true:
           j = j * 10 + ord(frmt[i]) - ord('0')
           inc(i)
-          if frmt[i] notin {'0'..'9'}: break
+          if i >= frmt.len or frmt[i] notin {'0'..'9'}: break
         num = j
         if j > high(args) + 1:
           errorHandler(rInvalidFormatStr, $(j))
diff --git a/compiler/scriptconfig.nim b/compiler/scriptconfig.nim
index 692a8d896..30e5e4803 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
+  os, times, osproc, wordrecg, strtabs, modulegraphs, configuration
 
 # we support 'cmpIgnoreStyle' natively for efficiency:
 from strutils import cmpIgnoreStyle, contains
@@ -26,11 +26,12 @@ proc listDirs(a: VmArgs, filter: set[PathComponent]) =
   setResult(a, result)
 
 proc setupVM*(module: PSym; cache: IdentCache; scriptName: string;
-              config: ConfigRef = nil): PEvalContext =
+              graph: ModuleGraph): PEvalContext =
   # For Nimble we need to export 'setupVM'.
-  result = newCtx(module, cache)
+  result = newCtx(module, cache, graph)
   result.mode = emRepl
   registerAdditionalOps(result)
+  let conf = graph.config
 
   # captured vars:
   var errorMsg: string
@@ -98,13 +99,13 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string;
   cbconf thisDir:
     setResult(a, vthisDir)
   cbconf put:
-    options.setConfigVar(getString(a, 0), getString(a, 1))
+    options.setConfigVar(conf, getString(a, 0), getString(a, 1))
   cbconf get:
-    setResult(a, options.getConfigVar(a.getString 0))
+    setResult(a, options.getConfigVar(conf, a.getString 0))
   cbconf exists:
-    setResult(a, options.existsConfigVar(a.getString 0))
+    setResult(a, options.existsConfigVar(conf, a.getString 0))
   cbconf nimcacheDir:
-    setResult(a, options.getNimcacheDir())
+    setResult(a, options.getNimcacheDir(conf))
   cbconf paramStr:
     setResult(a, os.paramStr(int a.getInt 0))
   cbconf paramCount:
@@ -114,67 +115,66 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string;
   cbconf cmpIgnoreCase:
     setResult(a, strutils.cmpIgnoreCase(a.getString 0, a.getString 1))
   cbconf setCommand:
-    options.command = a.getString 0
+    conf.command = a.getString 0
     let arg = a.getString 1
     if arg.len > 0:
-      gProjectName = arg
+      conf.projectName = arg
       let path =
-        if gProjectName.isAbsolute: gProjectName
-        else: gProjectPath / gProjectName
+        if conf.projectName.isAbsolute: conf.projectName
+        else: conf.projectPath / conf.projectName
       try:
-        gProjectFull = canonicalizePath(path)
+        conf.projectFull = canonicalizePath(conf, path)
       except OSError:
-        gProjectFull = path
+        conf.projectFull = path
   cbconf getCommand:
-    setResult(a, options.command)
+    setResult(a, conf.command)
   cbconf switch:
-    processSwitch(a.getString 0, a.getString 1, passPP, module.info)
+    processSwitch(a.getString 0, a.getString 1, passPP, module.info, conf)
   cbconf hintImpl:
     processSpecificNote(a.getString 0, wHint, passPP, module.info,
-      a.getString 1)
+      a.getString 1, conf)
   cbconf warningImpl:
     processSpecificNote(a.getString 0, wWarning, passPP, module.info,
-      a.getString 1)
+      a.getString 1, conf)
   cbconf patchFile:
     let key = a.getString(0) & "_" & a.getString(1)
     var val = a.getString(2).addFileExt(NimExt)
     if {'$', '~'} in val:
-      val = pathSubs(val, vthisDir)
+      val = pathSubs(conf, val, vthisDir)
     elif not isAbsolute(val):
       val = vthisDir / val
-    gModuleOverrides[key] = val
+    conf.moduleOverrides[key] = val
   cbconf selfExe:
     setResult(a, os.getAppFilename())
   cbconf cppDefine:
-    if config != nil:
-      options.cppDefine(config, a.getString(0))
+    options.cppDefine(conf, a.getString(0))
 
 proc runNimScript*(cache: IdentCache; scriptName: string;
-                   freshDefines=true; config: ConfigRef=nil) =
-  rawMessage(hintConf, scriptName)
+                   freshDefines=true; conf: ConfigRef) =
+  rawMessage(conf, hintConf, scriptName)
   passes.gIncludeFile = includeModule
   passes.gImportModule = importModule
-  let graph = newModuleGraph(config)
-  if freshDefines: initDefines()
+  let graph = newModuleGraph(conf)
+  if freshDefines: initDefines(conf.symbols)
 
-  defineSymbol("nimscript")
-  defineSymbol("nimconfig")
+  defineSymbol(conf.symbols, "nimscript")
+  defineSymbol(conf.symbols, "nimconfig")
   registerPass(semPass)
   registerPass(evalPass)
 
-  searchPaths.add(options.libpath)
+  conf.searchPaths.add(conf.libpath)
 
   var m = graph.makeModule(scriptName)
   incl(m.flags, sfMainModule)
-  vm.globalCtx = setupVM(m, cache, scriptName, config)
+  vm.globalCtx = setupVM(m, cache, scriptName, graph)
 
   graph.compileSystemModule(cache)
   discard graph.processModule(m, llStreamOpen(scriptName, fmRead), nil, cache)
 
   # ensure we load 'system.nim' again for the real non-config stuff!
-  resetSystemArtifacts()
+  resetSystemArtifacts(graph)
   vm.globalCtx = nil
   # do not remove the defined symbols
   #initDefines()
-  undefSymbol("nimscript")
-  undefSymbol("nimconfig")
+  undefSymbol(conf.symbols, "nimscript")
+  undefSymbol(conf.symbols, "nimconfig")
diff --git a/compiler/sem.nim b/compiler/sem.nim
index 4fef1bc60..c5c3dd99b 100644
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -16,7 +16,7 @@ import
   procfind, lookups, rodread, 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
+  semparallel, lowerings, pluginsupport, plugins.active, rod, configuration
 
 from modulegraphs import ModuleGraph
 
@@ -33,7 +33,7 @@ proc semExprNoDeref(c: PContext, n: PNode, flags: TExprFlags = {}): PNode
 proc semProcBody(c: PContext, n: PNode): PNode
 
 proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode
-proc changeType(n: PNode, newType: PType, check: bool)
+proc changeType(c: PContext; n: PNode, newType: PType, check: bool)
 
 proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode
 proc semTypeNode(c: PContext, n: PNode, prev: PType): PType
@@ -64,14 +64,14 @@ template semIdeForTemplateOrGeneric(c: PContext; n: PNode;
   # templates perform some quick check whether the cursor is actually in
   # the generic or template.
   when defined(nimsuggest):
-    if gCmd == cmdIdeTools and requiresCheck:
+    if c.config.cmd == cmdIdeTools and requiresCheck:
       #if optIdeDebug in gGlobalOptions:
       #  echo "passing to safeSemExpr: ", renderTree(n)
       discard safeSemExpr(c, n)
 
 proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode =
   if arg.typ.isNil:
-    localError(arg.info, errExprXHasNoType,
+    localError(c.config, arg.info, "expression has no type: " &
                renderTree(arg, {renderNoComments}))
     # error correction:
     result = copyTree(arg)
@@ -79,14 +79,14 @@ proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode =
   else:
     result = indexTypesMatch(c, formal, arg.typ, arg)
     if result == nil:
-      typeMismatch(info, formal, arg.typ)
+      typeMismatch(c.config, info, formal, arg.typ)
       # error correction:
       result = copyTree(arg)
       result.typ = formal
     else:
       let x = result.skipConv
       if x.kind in {nkPar, nkTupleConstr} and formal.kind != tyExpr:
-        changeType(x, formal, check=true)
+        changeType(c, x, formal, check=true)
       else:
         result = skipHiddenSubConv(result)
         #result.typ = takeType(formal, arg.typ)
@@ -153,8 +153,9 @@ proc commonType*(x, y: PType): PType =
     if a.kind in {tyRef, tyPtr}:
       k = a.kind
       if b.kind != a.kind: return x
-      a = a.lastSon
-      b = b.lastSon
+      # bug #7601, array construction of ptr generic
+      a = a.lastSon.skipTypes({tyGenericInst})
+      b = b.lastSon.skipTypes({tyGenericInst})
     if a.kind == tyObject and b.kind == tyObject:
       result = commonSuperclass(a, b)
       # this will trigger an error later:
@@ -179,7 +180,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(n), getCurrOwner(c), n.info)
+  result = newSym(kind, considerQuotedIdent(c.config, n), getCurrOwner(c), n.info)
   when defined(nimsuggest):
     suggestDecl(c, n, result)
 
@@ -191,7 +192,7 @@ proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym =
     # and sfGenSym in n.sym.flags:
     result = n.sym
     if result.kind != kind:
-      localError(n.info, "cannot use symbol of kind '" &
+      localError(c.config, n.info, "cannot use symbol of kind '" &
                  $result.kind & "' as a '" & $kind & "'")
     if sfGenSym in result.flags and result.kind notin {skTemplate, skMacro, skParam}:
       # declarative context, so produce a fresh gensym:
@@ -203,7 +204,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(n), getCurrOwner(c), n.info)
+    result = newSym(kind, considerQuotedIdent(c.config, n), getCurrOwner(c), n.info)
   #if kind in {skForVar, skLet, skVar} and result.owner.kind == skModule:
   #  incl(result.flags, sfGlobal)
   when defined(nimsuggest):
@@ -215,20 +216,20 @@ proc semIdentVis(c: PContext, kind: TSymKind, n: PNode,
 proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode,
                         allowed: TSymFlags): PSym
 
-proc typeAllowedCheck(info: TLineInfo; typ: PType; kind: TSymKind;
+proc typeAllowedCheck(conf: ConfigRef; info: TLineInfo; typ: PType; kind: TSymKind;
                       flags: TTypeAllowedFlags = {}) =
   let t = typeAllowed(typ, kind, flags)
   if t != nil:
     if t == typ:
-      localError(info, "invalid type: '" & typeToString(typ) &
+      localError(conf, info, "invalid type: '" & typeToString(typ) &
         "' for " & substr($kind, 2).toLowerAscii)
     else:
-      localError(info, "invalid type: '" & typeToString(t) &
+      localError(conf, info, "invalid type: '" & typeToString(t) &
         "' in this context: '" & typeToString(typ) &
         "' for " & substr($kind, 2).toLowerAscii)
 
 proc paramsTypeCheck(c: PContext, typ: PType) {.inline.} =
-  typeAllowedCheck(typ.n.info, typ, skProc)
+  typeAllowedCheck(c.config, typ.n.info, typ, skProc)
 
 proc expectMacroOrTemplateCall(c: PContext, n: PNode): PSym
 proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode
@@ -281,10 +282,10 @@ proc fixupTypeAfterEval(c: PContext, evaluated, eOrig: PNode): PNode =
       result = evaluated
       let expectedType = eOrig.typ.skipTypes({tyStatic})
       if hasCycle(result):
-        globalError(eOrig.info, "the resulting AST is cyclic and cannot be processed further")
+        globalError(c.config, eOrig.info, "the resulting AST is cyclic and cannot be processed further")
         result = errorNode(c, eOrig)
       else:
-        semmacrosanity.annotateType(result, expectedType)
+        semmacrosanity.annotateType(result, expectedType, c.config)
   else:
     result = semExprWithType(c, evaluated)
     #result = fitNode(c, e.typ, result) inlined with special case:
@@ -301,18 +302,18 @@ proc tryConstExpr(c: PContext, n: PNode): PNode =
   var e = semExprWithType(c, n)
   if e == nil: return
 
-  result = getConstExpr(c.module, e)
+  result = getConstExpr(c.module, e, c.graph)
   if result != nil: return
 
-  let oldErrorCount = msgs.gErrorCounter
-  let oldErrorMax = msgs.gErrorMax
+  let oldErrorCount = c.config.errorCounter
+  let oldErrorMax = c.config.errorMax
   let oldErrorOutputs = errorOutputs
 
   errorOutputs = {}
-  msgs.gErrorMax = high(int)
+  c.config.errorMax = high(int)
 
   try:
-    result = evalConstExpr(c.module, c.cache, e)
+    result = evalConstExpr(c.module, c.cache, c.graph, e)
     if result == nil or result.kind == nkEmpty:
       result = nil
     else:
@@ -321,26 +322,29 @@ proc tryConstExpr(c: PContext, n: PNode): PNode =
   except ERecoverableError:
     result = nil
 
-  msgs.gErrorCounter = oldErrorCount
-  msgs.gErrorMax = oldErrorMax
+  c.config.errorCounter = oldErrorCount
+  c.config.errorMax = oldErrorMax
   errorOutputs = oldErrorOutputs
 
+const
+  errConstExprExpected = "constant expression expected"
+
 proc semConstExpr(c: PContext, n: PNode): PNode =
   var e = semExprWithType(c, n)
   if e == nil:
-    localError(n.info, errConstExprExpected)
+    localError(c.config, n.info, errConstExprExpected)
     return n
-  result = getConstExpr(c.module, e)
+  result = getConstExpr(c.module, e, c.graph)
   if result == nil:
     #if e.kind == nkEmpty: globalError(n.info, errConstExprExpected)
-    result = evalConstExpr(c.module, c.cache, e)
+    result = evalConstExpr(c.module, c.cache, c.graph, e)
     if result == nil or result.kind == nkEmpty:
       if e.info != n.info:
         pushInfoContext(n.info)
-        localError(e.info, errConstExprExpected)
+        localError(c.config, e.info, errConstExprExpected)
         popInfoContext()
       else:
-        localError(e.info, errConstExprExpected)
+        localError(c.config, e.info, errConstExprExpected)
       # error correction:
       result = e
     else:
@@ -355,7 +359,7 @@ proc semExprFlagDispatched(c: PContext, n: PNode, flags: TExprFlags): PNode =
   else:
     result = semExprWithType(c, n, flags)
     if efPreferStatic in flags:
-      var evaluated = getConstExpr(c.module, result)
+      var evaluated = getConstExpr(c.module, result, c.graph)
       if evaluated != nil: return evaluated
       evaluated = evalAtCompileTime(c, result)
       if evaluated != nil: return evaluated
@@ -377,8 +381,8 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode,
   ## reassigned, and binding the unbound identifiers that the macro output
   ## contains.
   inc(evalTemplateCounter)
-  if evalTemplateCounter > 100:
-    globalError(s.info, errTemplateInstantiationTooNested)
+  if evalTemplateCounter > evalTemplateLimit:
+    globalError(c.config, s.info, "template instantiation too nested")
   c.friendModules.add(s.owner.getModule)
 
   result = macroResult
@@ -420,43 +424,46 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode,
   dec(evalTemplateCounter)
   discard c.friendModules.pop()
 
+const
+  errMissingGenericParamsForTemplate = "'$1' has unspecified generic parameters"
+
 proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
                   flags: TExprFlags = {}): PNode =
   pushInfoContext(nOrig.info)
 
-  markUsed(n.info, sym, c.graph.usageSym)
+  markUsed(c.config, n.info, sym, c.graph.usageSym)
   styleCheckUse(n.info, sym)
   if sym == c.p.owner:
-    globalError(n.info, errRecursiveDependencyX, sym.name.s)
+    globalError(c.config, n.info, "recursive dependency: '$1'" % sym.name.s)
 
   let genericParams = if sfImmediate in sym.flags: 0
                       else: sym.ast[genericParamsPos].len
   let suppliedParams = max(n.safeLen - 1, 0)
 
   if suppliedParams < genericParams:
-    globalError(n.info, errMissingGenericParamsForTemplate, n.renderTree)
+    globalError(c.config, n.info, errMissingGenericParamsForTemplate % n.renderTree)
 
   #if c.evalContext == nil:
   #  c.evalContext = c.createEvalContext(emStatic)
-  result = evalMacroCall(c.module, c.cache, n, nOrig, sym)
+  result = evalMacroCall(c.module, c.cache, c.graph, n, nOrig, sym)
   if efNoSemCheck notin flags:
     result = semAfterMacroCall(c, n, result, sym, flags)
   result = wrapInComesFrom(nOrig.info, sym, result)
   popInfoContext()
 
 proc forceBool(c: PContext, n: PNode): PNode =
-  result = fitNode(c, getSysType(tyBool), n, n.info)
+  result = fitNode(c, getSysType(c.graph, n.info, tyBool), n, n.info)
   if result == nil: result = n
 
 proc semConstBoolExpr(c: PContext, n: PNode): PNode =
   let nn = semExprWithType(c, n)
-  result = fitNode(c, getSysType(tyBool), nn, nn.info)
+  result = fitNode(c, getSysType(c.graph, n.info, tyBool), nn, nn.info)
   if result == nil:
-    localError(n.info, errConstExprExpected)
+    localError(c.config, n.info, errConstExprExpected)
     return nn
-  result = getConstExpr(c.module, result)
+  result = getConstExpr(c.module, result, c.graph)
   if result == nil:
-    localError(n.info, errConstExprExpected)
+    localError(c.config, n.info, errConstExprExpected)
     result = nn
 
 proc semGenericStmt(c: PContext, n: PNode): PNode
@@ -469,14 +476,14 @@ proc addCodeForGenerics(c: PContext, n: PNode) =
     var prc = c.generics[i].inst.sym
     if prc.kind in {skProc, skFunc, skMethod, skConverter} and prc.magic == mNone:
       if prc.ast == nil or prc.ast.sons[bodyPos] == nil:
-        internalError(prc.info, "no code for " & prc.name.s)
+        internalError(c.config, prc.info, "no code for " & prc.name.s)
       else:
         addSon(n, prc.ast)
   c.lastGenericIdx = c.generics.len
 
 proc myOpen(graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext =
   var c = newContext(graph, module, cache)
-  if c.p != nil: internalError(module.info, "sem.myOpen")
+  if c.p != nil: internalError(graph.config, module.info, "sem.myOpen")
   c.semConstExpr = semConstExpr
   c.semExpr = semExpr
   c.semTryExpr = tryExpr
@@ -494,14 +501,14 @@ proc myOpen(graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext =
   c.importTable = openScope(c)
   c.importTable.addSym(module) # a module knows itself
   if sfSystemModule in module.flags:
-    magicsys.systemModule = module # set global variable!
+    graph.systemModule = module
   c.topLevelScope = openScope(c)
   # don't be verbose unless the module belongs to the main package:
   if module.owner.id == gMainPackageId:
-    gNotes = gMainPackageNotes
+    graph.config.notes = graph.config.mainPackageNotes
   else:
-    if gMainPackageNotes == {}: gMainPackageNotes = gNotes
-    gNotes = ForeignPackageNotes
+    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 =
@@ -510,30 +517,30 @@ proc myOpenCached(graph: ModuleGraph; module: PSym; rd: PRodReader): PPassContex
 proc replayMethodDefs(graph: ModuleGraph; rd: PRodReader) =
   for m in items(rd.methods): methodDef(graph, m, true)
 
-proc isImportSystemStmt(n: PNode): bool =
-  if magicsys.systemModule == nil: return false
+proc isImportSystemStmt(g: ModuleGraph; n: PNode): bool =
+  if g.systemModule == nil: return false
   case n.kind
   of nkImportStmt:
     for x in n:
       if x.kind == nkIdent:
-        let f = checkModuleName(x, false)
-        if f == magicsys.systemModule.info.fileIndex:
+        let f = checkModuleName(g.config, x, false)
+        if f == g.systemModule.info.fileIndex:
           return true
   of nkImportExceptStmt, nkFromStmt:
     if n[0].kind == nkIdent:
-      let f = checkModuleName(n[0], false)
-      if f == magicsys.systemModule.info.fileIndex:
+      let f = checkModuleName(g.config, n[0], false)
+      if f == g.systemModule.info.fileIndex:
         return true
   else: discard
 
 proc semStmtAndGenerateGenerics(c: PContext, n: PNode): PNode =
   if n.kind == nkDefer:
-    localError(n.info, "defer statement not supported at top level")
-  if c.topStmts == 0 and not isImportSystemStmt(n):
+    localError(c.config, n.info, "defer statement not supported at top level")
+  if c.topStmts == 0 and not isImportSystemStmt(c.graph, n):
     if sfSystemModule notin c.module.flags and
         n.kind notin {nkEmpty, nkCommentStmt}:
-      c.importTable.addSym magicsys.systemModule # import the "System" identifier
-      importAllSymbols(c, magicsys.systemModule)
+      c.importTable.addSym c.graph.systemModule # import the "System" identifier
+      importAllSymbols(c, c.graph.systemModule)
       inc c.topStmts
   else:
     inc c.topStmts
@@ -555,11 +562,11 @@ proc semStmtAndGenerateGenerics(c: PContext, n: PNode): PNode =
         if result.kind != nkEmpty: addSon(a, result)
         result = a
   result = hloStmt(c, result)
-  if gCmd == cmdInteractive and not isEmptyType(result.typ):
+  if c.config.cmd == cmdInteractive and not isEmptyType(result.typ):
     result = buildEchoStmt(c, result)
-  if gCmd == cmdIdeTools:
+  if c.config.cmd == cmdIdeTools:
     appendToModule(c.module, result)
-  result = transformStmt(c.module, result)
+  result = transformStmt(c.graph, c.module, result)
 
 proc recoverContext(c: PContext) =
   # clean up in case of a semantic error: We clean up the stacks, etc. This is
@@ -572,7 +579,7 @@ proc recoverContext(c: PContext) =
 proc myProcess(context: PPassContext, n: PNode): PNode =
   var c = PContext(context)
   # no need for an expensive 'try' if we stop after the first error anyway:
-  if msgs.gErrorMax <= 1:
+  if c.config.errorMax <= 1:
     result = semStmtAndGenerateGenerics(c, n)
   else:
     let oldContextLen = msgs.getInfoContextLen()
@@ -588,16 +595,16 @@ proc myProcess(context: PPassContext, n: PNode): PNode =
         result = nil
       else:
         result = ast.emptyNode
-      #if gCmd == cmdIdeTools: findSuggest(c, n)
+      #if c.config.cmd == cmdIdeTools: findSuggest(c, n)
   rod.storeNode(c.module, result)
 
 proc testExamples(c: PContext) =
   let inp = toFullPath(c.module.info)
   let outp = inp.changeFileExt"" & "_examples.nim"
   renderModule(c.runnableExamples, inp, outp)
-  let backend = if isDefined("js"): "js"
-                elif isDefined("cpp"): "cpp"
-                elif isDefined("objc"): "objc"
+  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:
     quit "[Examples] failed"
@@ -605,13 +612,13 @@ proc testExamples(c: PContext) =
 
 proc myClose(graph: ModuleGraph; context: PPassContext, n: PNode): PNode =
   var c = PContext(context)
-  if gCmd == cmdIdeTools and not c.suggestionsMade:
+  if c.config.cmd == cmdIdeTools and not c.suggestionsMade:
     suggestSentinel(c)
   closeScope(c)         # close module's scope
   rawCloseScope(c)      # imported symbols; don't check for unused ones!
   result = newNode(nkStmtList)
   if n != nil:
-    internalError(n.info, "n is not nil") #result := n;
+    internalError(c.config, n.info, "n is not nil") #result := n;
   addCodeForGenerics(c, result)
   if c.module.ast != nil:
     result.add(c.module.ast)
diff --git a/compiler/semasgn.nim b/compiler/semasgn.nim
index bbd2baf6e..f0fde195c 100644
--- a/compiler/semasgn.nim
+++ b/compiler/semasgn.nim
@@ -33,7 +33,7 @@ proc at(a, i: PNode, elemType: PType): PNode =
 
 proc liftBodyTup(c: var TLiftCtx; t: PType; body, x, y: PNode) =
   for i in 0 ..< t.len:
-    let lit = lowerings.newIntLit(i)
+    let lit = lowerings.newIntLit(c.c.graph, x.info, i)
     liftBodyAux(c, t.sons[i], body, x.at(lit, t.sons[i]), y.at(lit, t.sons[i]))
 
 proc dotField(x: PNode, f: PSym): PNode =
@@ -66,15 +66,15 @@ proc liftBodyObj(c: var TLiftCtx; n, body, x, y: PNode) =
       liftBodyObj(c, n[i].lastSon, branch.sons[L-1], x, y)
       caseStmt.add(branch)
     body.add(caseStmt)
-    localError(c.info, "cannot lift assignment operator to 'case' object")
+    localError(c.c.config, c.info, "cannot lift assignment operator to 'case' object")
   of nkRecList:
     for t in items(n): liftBodyObj(c, t, body, x, y)
   else:
-    illFormedAstLocal(n)
+    illFormedAstLocal(n, c.c.config)
 
 proc genAddr(c: PContext; x: PNode): PNode =
   if x.kind == nkHiddenDeref:
-    checkSonsLen(x, 1)
+    checkSonsLen(x, 1, c.config)
     result = x.sons[0]
   else:
     result = newNodeIT(nkHiddenAddr, x.info, makeVarType(c, x.typ))
@@ -82,7 +82,7 @@ proc genAddr(c: PContext; x: PNode): PNode =
 
 proc newAsgnCall(c: PContext; op: PSym; x, y: PNode): PNode =
   if sfError in op.flags:
-    localError(x.info, errWrongSymbolX, op.name.s)
+    localError(c.config, x.info, "usage of '$1' is a user-defined error" % op.name.s)
   result = newNodeI(nkCall, x.info)
   result.add newSymNode(op)
   result.add genAddr(c, x)
@@ -101,7 +101,7 @@ proc newOpCall(op: PSym; x: PNode): PNode =
 proc destructorCall(c: PContext; op: PSym; x: PNode): PNode =
   result = newNodeIT(nkCall, x.info, op.typ.sons[0])
   result.add(newSymNode(op))
-  if newDestructors:
+  if destructor in c.features:
     result.add genAddr(c, x)
   else:
     result.add x
@@ -124,7 +124,7 @@ proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode;
       op = field
       if op == nil:
         op = liftBody(c.c, t, c.kind, c.info)
-    markUsed(c.info, op, c.c.graph.usageSym)
+    markUsed(c.c.config, c.info, op, c.c.graph.usageSym)
     styleCheckUse(c.info, op)
     body.add newAsgnCall(c.c, op, x, y)
     result = true
@@ -134,7 +134,7 @@ proc considerOverloadedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
   of attachedDestructor:
     let op = t.destructor
     if op != nil:
-      markUsed(c.info, op, c.c.graph.usageSym)
+      markUsed(c.c.config, c.info, op, c.c.graph.usageSym)
       styleCheckUse(c.info, op)
       body.add destructorCall(c.c, op, x)
       result = true
@@ -145,7 +145,7 @@ proc considerOverloadedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
   of attachedDeepCopy:
     let op = t.deepCopy
     if op != nil:
-      markUsed(c.info, op, c.c.graph.usageSym)
+      markUsed(c.c.config, c.info, op, c.c.graph.usageSym)
       styleCheckUse(c.info, op)
       body.add newDeepCopyCall(op, x, y)
       result = true
@@ -163,37 +163,37 @@ proc addVar(father, v, value: PNode) =
 
 proc declareCounter(c: var TLiftCtx; body: PNode; first: BiggestInt): PNode =
   var temp = newSym(skTemp, getIdent(lowerings.genPrefix), c.fn, c.info)
-  temp.typ = getSysType(tyInt)
+  temp.typ = getSysType(c.c.graph, body.info, tyInt)
   incl(temp.flags, sfFromGeneric)
 
   var v = newNodeI(nkVarSection, c.info)
   result = newSymNode(temp)
-  v.addVar(result, lowerings.newIntLit(first))
+  v.addVar(result, lowerings.newIntLit(c.c.graph, body.info, first))
   body.add v
 
-proc genBuiltin(magic: TMagic; name: string; i: PNode): PNode =
+proc genBuiltin(g: ModuleGraph; magic: TMagic; name: string; i: PNode): PNode =
   result = newNodeI(nkCall, i.info)
-  result.add createMagic(name, magic).newSymNode
+  result.add createMagic(g, name, magic).newSymNode
   result.add i
 
 proc genWhileLoop(c: var TLiftCtx; i, dest: PNode): PNode =
   result = newNodeI(nkWhileStmt, c.info, 2)
-  let cmp = genBuiltin(mLeI, "<=", i)
-  cmp.add genHigh(dest)
-  cmp.typ = getSysType(tyBool)
+  let cmp = genBuiltin(c.c.graph, mLeI, "<=", i)
+  cmp.add genHigh(c.c.graph, dest)
+  cmp.typ = getSysType(c.c.graph, c.info, tyBool)
   result.sons[0] = cmp
   result.sons[1] = newNodeI(nkStmtList, c.info)
 
-proc addIncStmt(body, i: PNode) =
-  let incCall = genBuiltin(mInc, "inc", i)
-  incCall.add lowerings.newIntLit(1)
+proc addIncStmt(c: var TLiftCtx; body, i: PNode) =
+  let incCall = genBuiltin(c.c.graph, mInc, "inc", i)
+  incCall.add lowerings.newIntLit(c.c.graph, c.info, 1)
   body.add incCall
 
 proc newSeqCall(c: PContext; x, y: PNode): PNode =
   # don't call genAddr(c, x) here:
-  result = genBuiltin(mNewSeq, "newSeq", x)
-  let lenCall = genBuiltin(mLengthSeq, "len", y)
-  lenCall.typ = getSysType(tyInt)
+  result = genBuiltin(c.graph, mNewSeq, "newSeq", x)
+  let lenCall = genBuiltin(c.graph, mLengthSeq, "len", y)
+  lenCall.typ = getSysType(c.graph, x.info, tyInt)
   result.add lenCall
 
 proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) =
@@ -212,7 +212,7 @@ proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) =
       let elemType = t.lastSon
       liftBodyAux(c, elemType, whileLoop.sons[1], x.at(i, elemType),
                                                   y.at(i, elemType))
-      addIncStmt(whileLoop.sons[1], i)
+      addIncStmt(c, whileLoop.sons[1], i)
       body.add whileLoop
     else:
       defaultOp(c, t, body, x, y)
@@ -231,20 +231,20 @@ proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) =
       # have to go through some indirection; we delegate this to the codegen:
       let call = newNodeI(nkCall, c.info, 2)
       call.typ = t
-      call.sons[0] = newSymNode(createMagic("deepCopy", mDeepCopy))
+      call.sons[0] = newSymNode(createMagic(c.c.graph, "deepCopy", mDeepCopy))
       call.sons[1] = y
       body.add newAsgnStmt(x, call)
   of tyVarargs, tyOpenArray:
-    localError(c.info, errGenerated, "cannot copy openArray")
+    localError(c.c.config, c.info, "cannot copy openArray")
   of tyFromExpr, tyProxy, tyBuiltInTypeClass, tyUserTypeClass,
      tyUserTypeClassInst, tyCompositeTypeClass, tyAnd, tyOr, tyNot, tyAnything,
      tyGenericParam, tyGenericBody, tyNil, tyExpr, tyStmt,
      tyTypeDesc, tyGenericInvocation, tyForward:
-    internalError(c.info, "assignment requested for type: " & typeToString(t))
+    internalError(c.c.config, c.info, "assignment requested for type: " & typeToString(t))
   of tyOrdinal, tyRange, tyInferred,
      tyGenericInst, tyStatic, tyVar, tyLent, tyAlias, tySink:
     liftBodyAux(c, lastSon(t), body, x, y)
-  of tyUnused, tyOptAsRef: internalError("liftBodyAux")
+  of tyUnused, tyOptAsRef: internalError(c.c.config, "liftBodyAux")
 
 proc newProcType(info: TLineInfo; owner: PSym): PType =
   result = newType(tyProc, owner)
@@ -319,7 +319,7 @@ proc liftTypeBoundOps*(c: PContext; typ: PType; info: TLineInfo) =
   ## In the semantic pass this is called in strategic places
   ## to ensure we lift assignment, destructors and moves properly.
   ## The later 'destroyer' pass depends on it.
-  if not newDestructors or not hasDestructor(typ): return
+  if destructor notin c.features or not hasDestructor(typ): return
   when false:
     # do not produce wrong liftings while we're still instantiating generics:
     # now disabled; breaks topttree.nim!
diff --git a/compiler/semcall.nim b/compiler/semcall.nim
index 0fc12f164..e9a31d3d6 100644
--- a/compiler/semcall.nim
+++ b/compiler/semcall.nim
@@ -59,7 +59,8 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
                        filter: TSymKinds,
                        best, alt: var TCandidate,
                        errors: var CandidateErrors,
-                       diagnosticsFlag = false) =
+                       diagnosticsFlag: bool,
+                       errorsEnabled: bool) =
   var o: TOverloadIter
   var sym = initOverloadIter(o, c, headSymbol)
   var scope = o.lastOverloadScope
@@ -68,6 +69,7 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
   # This can occur in cases like 'init(a, 1, (var b = new(Type2); b))'
   let counterInitial = c.currentScope.symbols.counter
   var syms: seq[tuple[s: PSym, scope: int]]
+  var noSyms = true
   var nextSymIndex = 0
   while sym != nil:
     if sym.kind in filter:
@@ -102,7 +104,7 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
           var cmp = cmpCandidates(best, z)
           if cmp < 0: best = z   # x is better than the best so far
           elif cmp == 0: alt = z # x is as good as the best so far
-      elif errors != nil or z.diagnostics != nil:
+      elif errorsEnabled or z.diagnosticsEnabled:
         errors.safeAdd(CandidateError(
           sym: sym,
           unmatchedVarParam: int z.mutabilityProblem,
@@ -113,7 +115,8 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
       # before any further candidate init and compare. SLOW, but rare case.
       syms = initCandidateSymbols(c, headSymbol, initialBinding, filter,
                                   best, alt, o, diagnosticsFlag)
-    if syms == nil:
+      noSyms = false
+    if noSyms:
       sym = nextOverloadIter(o, c, headSymbol)
       scope = o.lastOverloadScope
     elif nextSymIndex < syms.len:
@@ -199,24 +202,31 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
 
   result = (prefer, candidates)
 
+const
+  errTypeMismatch = "type mismatch: got <"
+  errButExpected = "but expected one of: "
+  errUndeclaredField = "undeclared field: '$1'"
+  errUndeclaredRoutine = "attempting to call undeclared routine: '$1'"
+  errAmbiguousCallXYZ = "ambiguous call; both $1 and $2 match for: $3"
+
 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 == {}:
     # fail fast:
-    globalError(n.info, errTypeMismatch, "")
-  if errors.isNil or errors.len == 0:
-    localError(n.info, errExprXCannotBeCalled, n[0].renderTree)
+    globalError(c.config, n.info, "type mismatch")
+  if errors.len == 0:
+    localError(c.config, n.info, "expression '$1' cannot be called" % n[0].renderTree)
     return
 
   let (prefer, candidates) = presentFailedCandidates(c, n, errors)
-  var result = msgKindToString(errTypeMismatch)
+  var result = errTypeMismatch
   add(result, describeArgs(c, n, 1, prefer))
   add(result, '>')
   if candidates != "":
-    add(result, "\n" & msgKindToString(errButExpected) & "\n" & candidates)
-  localError(n.info, errGenerated, result & "\nexpression: " & $n)
+    add(result, "\n" & errButExpected & "\n" & candidates)
+  localError(c.config, n.info, result & "\nexpression: " & $n)
 
 proc bracketNotFoundError(c: PContext; n: PNode) =
   var errors: CandidateErrors = @[]
@@ -227,16 +237,18 @@ proc bracketNotFoundError(c: PContext; n: PNode) =
     if symx.kind in routineKinds:
       errors.add(CandidateError(sym: symx,
                                 unmatchedVarParam: 0, firstMismatch: 0,
-                                diagnostics: nil))
+                                diagnostics: nil,
+                                enabled: false))
     symx = nextOverloadIter(o, c, headSymbol)
   if errors.len == 0:
-    localError(n.info, "could not resolve: " & $n)
+    localError(c.config, n.info, "could not resolve: " & $n)
   else:
     notFoundError(c, n, errors)
 
 proc resolveOverloads(c: PContext, n, orig: PNode,
                       filter: TSymKinds, flags: TExprFlags,
-                      errors: var CandidateErrors): TCandidate =
+                      errors: var CandidateErrors,
+                      errorsEnabled: bool): TCandidate =
   var initialBinding: PNode
   var alt: TCandidate
   var f = n.sons[0]
@@ -249,7 +261,8 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
 
   template pickBest(headSymbol) =
     pickBestCandidate(c, headSymbol, n, orig, initialBinding,
-                      filter, result, alt, errors, efExplain in flags)
+                      filter, result, alt, errors, efExplain in flags,
+                      errorsEnabled)
   pickBest(f)
 
   let overloadsState = result.state
@@ -271,7 +284,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
       else: return
 
     if nfDotField in n.flags:
-      internalAssert f.kind == nkIdent and n.sonsLen >= 2
+      internalAssert c.config, f.kind == nkIdent and n.len >= 2
 
       # leave the op head symbol empty,
       # we are going to try multiple variants
@@ -300,13 +313,13 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
 
     if overloadsState == csEmpty and result.state == csEmpty:
       if nfDotField in n.flags and nfExplicitCall notin n.flags:
-        localError(n.info, errUndeclaredField, considerQuotedIdent(f, n).s)
+        localError(c.config, n.info, errUndeclaredField % considerQuotedIdent(c.config, f, n).s)
       else:
-        localError(n.info, errUndeclaredRoutine, considerQuotedIdent(f, n).s)
+        localError(c.config, n.info, errUndeclaredRoutine % considerQuotedIdent(c.config, f, n).s)
       return
     elif result.state != csMatch:
       if nfExprCall in n.flags:
-        localError(n.info, errExprXCannotBeCalled,
+        localError(c.config, n.info, "expression '$1' cannot be called" %
                    renderTree(n, {renderNoComments}))
       else:
         if {nfDotField, nfDotSetter} * n.flags != {}:
@@ -316,13 +329,13 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
       return
   if alt.state == csMatch and cmpCandidates(result, alt) == 0 and
       not sameMethodDispatcher(result.calleeSym, alt.calleeSym):
-    internalAssert result.state == csMatch
+    internalAssert c.config, result.state == csMatch
     #writeMatches(result)
     #writeMatches(alt)
     if errorOutputs == {}:
       # quick error message for performance of 'compiles' built-in:
-      globalError(n.info, errGenerated, "ambiguous call")
-    elif gErrorCounter == 0:
+      globalError(c.config, n.info, errGenerated, "ambiguous call")
+    elif c.config.errorCounter == 0:
       # don't cascade errors
       var args = "("
       for i in countup(1, sonsLen(n) - 1):
@@ -330,7 +343,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
         add(args, typeToString(n.sons[i].typ))
       add(args, ")")
 
-      localError(n.info, errGenerated, msgKindToString(errAmbiguousCallXYZ) % [
+      localError(c.config, n.info, errAmbiguousCallXYZ % [
         getProcHeader(result.calleeSym), getProcHeader(alt.calleeSym),
         args])
 
@@ -371,7 +384,7 @@ proc inferWithMetatype(c: PContext, formal: PType,
     result.typ = generateTypeInstance(c, m.bindings, arg.info,
                                       formal.skipTypes({tyCompositeTypeClass}))
   else:
-    typeMismatch(arg.info, formal, arg.typ)
+    typeMismatch(c.config, arg.info, formal, arg.typ)
     # error correction:
     result = copyTree(arg)
     result.typ = formal
@@ -379,7 +392,7 @@ proc inferWithMetatype(c: PContext, formal: PType,
 proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode =
   assert x.state == csMatch
   var finalCallee = x.calleeSym
-  markUsed(n.sons[0].info, finalCallee, c.graph.usageSym)
+  markUsed(c.config, n.sons[0].info, finalCallee, c.graph.usageSym)
   styleCheckUse(n.sons[0].info, finalCallee)
   assert finalCallee.ast != nil
   if x.hasFauxMatch:
@@ -405,7 +418,7 @@ proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode =
         of skType:
           x.call.add newSymNode(s, n.info)
         else:
-          internalAssert false
+          internalAssert c.config, false
 
   result = x.call
   instGenericConvertersSons(c, result, x)
@@ -423,18 +436,17 @@ proc tryDeref(n: PNode): PNode =
 
 proc semOverloadedCall(c: PContext, n, nOrig: PNode,
                        filter: TSymKinds, flags: TExprFlags): PNode =
-  var errors: CandidateErrors = if efExplain in flags: @[]
-                                else: nil
-  var r = resolveOverloads(c, n, nOrig, filter, flags, errors)
+  var errors: CandidateErrors = if efExplain in flags: @[] else: nil
+  var r = resolveOverloads(c, n, nOrig, filter, flags, errors, efExplain in flags)
   if r.state == csMatch:
     # this may be triggered, when the explain pragma is used
     if errors.len > 0:
       let (_, candidates) = presentFailedCandidates(c, n, errors)
-      message(n.info, hintUserRaw,
+      message(c.config, n.info, hintUserRaw,
               "Non-matching candidates for " & renderTree(n) & "\n" &
               candidates)
     result = semResolvedCall(c, n, r)
-  elif experimentalMode(c) and canDeref(n):
+  elif implicitDeref in c.features and canDeref(n):
     # try to deref the first argument and then try overloading resolution again:
     #
     # XXX: why is this here?
@@ -443,7 +455,7 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode,
     # into sigmatch with hidden conversion produced there
     #
     n.sons[1] = n.sons[1].tryDeref
-    var r = resolveOverloads(c, n, nOrig, filter, flags, errors)
+    var r = resolveOverloads(c, n, nOrig, filter, flags, errors, efExplain in flags)
     if r.state == csMatch: result = semResolvedCall(c, n, r)
     else:
       # get rid of the deref again for a better error message:
@@ -463,8 +475,8 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode,
     else:
       notFoundError(c, n, errors)
 
-proc explicitGenericInstError(n: PNode): PNode =
-  localError(n.info, errCannotInstantiateX, renderTree(n))
+proc explicitGenericInstError(c: PContext; n: PNode): PNode =
+  localError(c.config, n.info, errCannotInstantiateX % renderTree(n))
   result = n
 
 proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode =
@@ -479,7 +491,7 @@ proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode =
     if tm in {isNone, isConvertible}: return nil
   var newInst = generateInstance(c, s, m.bindings, n.info)
   newInst.typ.flags.excl tfUnresolved
-  markUsed(n.info, s, c.graph.usageSym)
+  markUsed(c.config, n.info, s, c.graph.usageSym)
   styleCheckUse(n.info, s)
   result = newSymNode(newInst, n.info)
 
@@ -495,11 +507,11 @@ proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
     # number of generic type parameters:
     if safeLen(s.ast.sons[genericParamsPos]) != n.len-1:
       let expected = safeLen(s.ast.sons[genericParamsPos])
-      localError(n.info, errGenerated, "cannot instantiate: " & renderTree(n) &
-         "; got " & $(n.len-1) & " type(s) but expected " & $expected)
+      localError(c.config, n.info, errGenerated, "cannot instantiate: '" & renderTree(n) &
+         "'; got " & $(n.len-1) & " type(s) but expected " & $expected)
       return n
     result = explicitGenericSym(c, n, s)
-    if result == nil: result = explicitGenericInstError(n)
+    if result == nil: result = explicitGenericInstError(c, n)
   elif a.kind in {nkClosedSymChoice, nkOpenSymChoice}:
     # choose the generic proc with the proper number of type parameters.
     # XXX I think this could be improved by reusing sigmatch.paramTypesMatch.
@@ -517,10 +529,10 @@ proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
     # get rid of nkClosedSymChoice if not ambiguous:
     if result.len == 1 and a.kind == nkClosedSymChoice:
       result = result[0]
-    elif result.len == 0: result = explicitGenericInstError(n)
-    # candidateCount != 1: return explicitGenericInstError(n)
+    elif result.len == 0: result = explicitGenericInstError(c, n)
+    # candidateCount != 1: return explicitGenericInstError(c, n)
   else:
-    result = explicitGenericInstError(n)
+    result = explicitGenericInstError(c, n)
 
 proc searchForBorrowProc(c: PContext, startScope: PScope, fn: PSym): PSym =
   # Searchs for the fn in the symbol table. If the parameter lists are suitable
diff --git a/compiler/semdata.nim b/compiler/semdata.nim
index 3996188dc..12ac19ca3 100644
--- a/compiler/semdata.nim
+++ b/compiler/semdata.nim
@@ -14,7 +14,7 @@ import
   wordrecg,
   ropes, msgs, platform, os, condsyms, idents, renderer, types, extccomp, math,
   magicsys, nversion, nimsets, parser, times, passes, rodread, vmdef,
-  modulegraphs
+  modulegraphs, configuration
 
 type
   TOptionEntry* = object      # entries to put on a stack for pragma parsing
@@ -63,7 +63,7 @@ type
       # to the user.
     efWantStmt, efAllowStmt, efDetermineType, efExplain,
     efAllowDestructor, efWantValue, efOperand, efNoSemCheck,
-    efNoProcvarCheck, efNoEvaluateGeneric, efInCall, efFromHlo,
+    efNoEvaluateGeneric, efInCall, efFromHlo
 
   TExprFlags* = set[TExprFlag]
 
@@ -89,6 +89,7 @@ type
     ambiguousSymbols*: IntSet  # ids of all ambiguous symbols (cannot
                                # store this info in the syms themselves!)
     inGenericContext*: int     # > 0 if we are in a generic type
+    inStaticContext*: int      # > 0 if we are inside a static: block
     inUnrolledContext*: int    # > 0 if we are unrolling a loop
     compilesContextId*: int    # > 0 if we are in a ``compiles`` magic
     compilesContextIdGenerator*: int
@@ -130,6 +131,7 @@ type
     signatures*: TStrTable
     recursiveDep*: string
     suggestionsMade*: bool
+    features*: set[Feature]
     inTypeContext*: int
     typesWithOps*: seq[(PType, PType)] #\
       # We need to instantiate the type bound ops lazily after
@@ -138,13 +140,15 @@ type
       # would otherwise fail.
     runnableExamples*: PNode
 
+template config*(c: PContext): ConfigRef = c.graph.config
+
 proc makeInstPair*(s: PSym, inst: PInstantiation): TInstantiationPair =
   result.genericSym = s
   result.inst = inst
 
 proc filename*(c: PContext): string =
   # the module's filename
-  return c.module.filename
+  return toFilename(FileIndex c.module.position)
 
 proc scopeDepth*(c: PContext): int {.inline.} =
   result = if c.currentScope != nil: c.currentScope.depthLevel
@@ -163,7 +167,7 @@ proc pushOwner*(c: PContext; owner: PSym) =
 proc popOwner*(c: PContext) =
   var length = len(c.graph.owners)
   if length > 0: setLen(c.graph.owners, length - 1)
-  else: internalError("popOwner")
+  else: internalError(c.config, "popOwner")
 
 proc lastOptionEntry*(c: PContext): POptionEntry =
   result = c.optionStack[^1]
@@ -199,19 +203,19 @@ proc considerGenSyms*(c: PContext; n: PNode) =
     for i in 0..<n.safeLen:
       considerGenSyms(c, n.sons[i])
 
-proc newOptionEntry*(): POptionEntry =
+proc newOptionEntry*(conf: ConfigRef): POptionEntry =
   new(result)
-  result.options = gOptions
+  result.options = conf.options
   result.defaultCC = ccDefault
   result.dynlib = nil
-  result.notes = gNotes
+  result.notes = conf.notes
 
 proc newContext*(graph: ModuleGraph; module: PSym; cache: IdentCache): PContext =
   new(result)
   result.ambiguousSymbols = initIntSet()
   result.optionStack = @[]
   result.libs = @[]
-  result.optionStack.add(newOptionEntry())
+  result.optionStack.add(newOptionEntry(graph.config))
   result.module = module
   result.friendModules = @[module]
   result.converters = @[]
@@ -225,7 +229,7 @@ proc newContext*(graph: ModuleGraph; module: PSym; cache: IdentCache): PContext
   result.graph = graph
   initStrTable(result.signatures)
   result.typesWithOps = @[]
-
+  result.features = graph.config.features
 
 proc inclSym(sq: var TSymSeq, s: PSym) =
   var L = len(sq)
@@ -254,7 +258,7 @@ proc newTypeS*(kind: TTypeKind, c: PContext): PType =
 
 proc makePtrType*(c: PContext, baseType: PType): PType =
   result = newTypeS(tyPtr, c)
-  addSonSkipIntLit(result, baseType.assertNotNil)
+  addSonSkipIntLit(result, baseType)
 
 proc makeTypeWithModifier*(c: PContext,
                            modifier: TTypeKind,
@@ -265,25 +269,26 @@ proc makeTypeWithModifier*(c: PContext,
     result = baseType
   else:
     result = newTypeS(modifier, c)
-    addSonSkipIntLit(result, baseType.assertNotNil)
+    addSonSkipIntLit(result, baseType)
 
 proc makeVarType*(c: PContext, baseType: PType; kind = tyVar): PType =
   if baseType.kind == kind:
     result = baseType
   else:
     result = newTypeS(kind, c)
-    addSonSkipIntLit(result, baseType.assertNotNil)
+    addSonSkipIntLit(result, baseType)
 
 proc makeTypeDesc*(c: PContext, typ: PType): PType =
   if typ.kind == tyTypeDesc:
     result = typ
   else:
     result = newTypeS(tyTypeDesc, c)
-    result.addSonSkipIntLit(typ.assertNotNil)
+    result.addSonSkipIntLit(typ)
 
 proc makeTypeSymNode*(c: PContext, typ: PType, info: TLineInfo): PNode =
   let typedesc = makeTypeDesc(c, typ)
-  let sym = newSym(skType, c.cache.idAnon, getCurrOwner(c), info).linkTo(typedesc)
+  let sym = newSym(skType, c.cache.idAnon, getCurrOwner(c), info,
+                   c.config.options).linkTo(typedesc)
   return newSymNode(sym, info)
 
 proc makeTypeFromExpr*(c: PContext, n: PNode): PType =
@@ -338,21 +343,20 @@ proc makeNotType*(c: PContext, t1: PType): PType =
   result.flags.incl(t1.flags * {tfHasStatic})
   result.flags.incl tfHasMeta
 
-proc nMinusOne*(n: PNode): PNode =
+proc nMinusOne(c: PContext; n: PNode): PNode =
   result = newNode(nkCall, n.info, @[
-    newSymNode(getSysMagic("pred", mPred)),
-    n])
+    newSymNode(getSysMagic(c.graph, n.info, "pred", mPred)), n])
 
 # Remember to fix the procs below this one when you make changes!
 proc makeRangeWithStaticExpr*(c: PContext, n: PNode): PType =
-  let intType = getSysType(tyInt)
+  let intType = getSysType(c.graph, n.info, tyInt)
   result = newTypeS(tyRange, c)
   result.sons = @[intType]
   if n.typ != nil and n.typ.n == nil:
     result.flags.incl tfUnresolved
   result.n = newNode(nkRange, n.info, @[
     newIntTypeNode(nkIntLit, 0, intType),
-    makeStaticExpr(c, n.nMinusOne)])
+    makeStaticExpr(c, nMinusOne(c, n))])
 
 template rangeHasUnresolvedStatic*(t: PType): bool =
   tfUnresolved in t.flags
@@ -371,7 +375,8 @@ proc fillTypeS*(dest: PType, kind: TTypeKind, c: PContext) =
   dest.size = - 1
 
 proc makeRangeType*(c: PContext; first, last: BiggestInt;
-                    info: TLineInfo; intType = getSysType(tyInt)): PType =
+                    info: TLineInfo; intType: PType = nil): PType =
+  let intType = if intType != nil: intType else: getSysType(c.graph, info, tyInt)
   var n = newNodeI(nkRange, info)
   addSon(n, newIntTypeNode(nkIntLit, first, intType))
   addSon(n, newIntTypeNode(nkIntLit, last, intType))
@@ -384,20 +389,17 @@ proc markIndirect*(c: PContext, s: PSym) {.inline.} =
     incl(s.flags, sfAddrTaken)
     # XXX add to 'c' for global analysis
 
-proc illFormedAst*(n: PNode) =
-  globalError(n.info, errIllFormedAstX, renderTree(n, {renderNoComments}))
+proc illFormedAst*(n: PNode; conf: ConfigRef) =
+  globalError(conf, n.info, errIllFormedAstX, renderTree(n, {renderNoComments}))
 
-proc illFormedAstLocal*(n: PNode) =
-  localError(n.info, errIllFormedAstX, renderTree(n, {renderNoComments}))
+proc illFormedAstLocal*(n: PNode; conf: ConfigRef) =
+  localError(conf, n.info, errIllFormedAstX, renderTree(n, {renderNoComments}))
 
-proc checkSonsLen*(n: PNode, length: int) =
-  if sonsLen(n) != length: illFormedAst(n)
+proc checkSonsLen*(n: PNode, length: int; conf: ConfigRef) =
+  if sonsLen(n) != length: illFormedAst(n, conf)
 
-proc checkMinSonsLen*(n: PNode, length: int) =
-  if sonsLen(n) < length: illFormedAst(n)
+proc checkMinSonsLen*(n: PNode, length: int; conf: ConfigRef) =
+  if sonsLen(n) < length: illFormedAst(n, conf)
 
 proc isTopLevel*(c: PContext): bool {.inline.} =
   result = c.currentScope.depthLevel <= 2
-
-proc experimentalMode*(c: PContext): bool {.inline.} =
-  result = gExperimentalMode or sfExperimental in c.module.flags
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 4256e0aa6..6b32fa66c 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -10,12 +10,24 @@
 # this module does the semantic checking for expressions
 # included from sem.nim
 
+const
+  errExprXHasNoType = "expression '$1' has no type (or is ambiguous)"
+  errXExpectsTypeOrValue = "'$1' expects a type or value"
+  errVarForOutParamNeededX = "for a 'var' type a variable needs to be passed; but '$1' is immutable"
+  errXStackEscape = "address of '$1' may not escape its stack frame"
+  errExprHasNoAddress = "expression has no address; maybe use 'unsafeAddr'"
+  errCannotInterpretNodeX = "cannot evaluate '$1'"
+  errNamedExprExpected = "named expression expected"
+  errNamedExprNotAllowed = "named expression not allowed here"
+  errFieldInitTwice = "field initialized twice: '$1'"
+  errUndeclaredFieldX = "undeclared field: '$1'"
+
 proc semTemplateExpr(c: PContext, n: PNode, s: PSym,
                      flags: TExprFlags = {}): PNode =
-  markUsed(n.info, s, c.graph.usageSym)
+  markUsed(c.config, n.info, s, c.graph.usageSym)
   styleCheckUse(n.info, s)
   pushInfoContext(n.info)
-  result = evalTemplate(n, s, getCurrOwner(c), efFromHlo in flags)
+  result = evalTemplate(n, s, getCurrOwner(c), c.config, efFromHlo in flags)
   if efNoSemCheck notin flags: result = semAfterMacroCall(c, n, result, s, flags)
   popInfoContext()
 
@@ -31,12 +43,12 @@ proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
   if result.typ != nil:
     # XXX tyGenericInst here?
     if result.typ.kind == tyProc and tfUnresolved in result.typ.flags:
-      localError(n.info, errProcHasNoConcreteType, n.renderTree)
+      localError(c.config, n.info, errProcHasNoConcreteType % n.renderTree)
     if result.typ.kind in {tyVar, tyLent}: result = newDeref(result)
   elif {efWantStmt, efAllowStmt} * flags != {}:
     result.typ = newTypeS(tyVoid, c)
   else:
-    localError(n.info, errExprXHasNoType,
+    localError(c.config, n.info, errExprXHasNoType %
                renderTree(result, {renderNoComments}))
     result.typ = errorType(c)
 
@@ -47,11 +59,10 @@ proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
     #raiseRecoverableError("")
     result = errorNode(c, n)
   if result.typ == nil or result.typ == enforceVoidContext:
-    localError(n.info, errExprXHasNoType,
+    localError(c.config, n.info, errExprXHasNoType %
                 renderTree(result, {renderNoComments}))
     result.typ = errorType(c)
   else:
-    if efNoProcvarCheck notin flags: semProcvarCheck(c, result)
     if result.typ.kind in {tyVar, tyLent}: result = newDeref(result)
 
 proc semExprNoDeref(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
@@ -60,19 +71,17 @@ proc semExprNoDeref(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
     # do not produce another redundant error message:
     result = errorNode(c, n)
   if result.typ == nil:
-    localError(n.info, errExprXHasNoType,
+    localError(c.config, n.info, errExprXHasNoType %
                renderTree(result, {renderNoComments}))
     result.typ = errorType(c)
-  else:
-    semProcvarCheck(c, result)
 
 proc semSymGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
   result = symChoice(c, n, s, scClosed)
 
-proc inlineConst(n: PNode, s: PSym): PNode {.inline.} =
+proc inlineConst(c: PContext, n: PNode, s: PSym): PNode {.inline.} =
   result = copyTree(s.ast)
   if result.isNil:
-    localError(n.info, "constant of type '" & typeToString(s.typ) & "' has no value")
+    localError(c.config, n.info, "constant of type '" & typeToString(s.typ) & "' has no value")
     result = newSymNode(s)
   else:
     result.typ = s.typ
@@ -175,7 +184,7 @@ proc maybeLiftType(t: var PType, c: PContext, info: TLineInfo) =
 
 proc semConv(c: PContext, n: PNode): PNode =
   if sonsLen(n) != 2:
-    localError(n.info, errConvNeedsOneArg)
+    localError(c.config, n.info, "a type conversion takes exactly one argument")
     return n
 
   result = newNodeI(nkConv, n.info)
@@ -195,7 +204,7 @@ proc semConv(c: PContext, n: PNode): PNode =
   # special case to make MyObject(x = 3) produce a nicer error message:
   if n[1].kind == nkExprEqExpr and
       targetType.skipTypes(abstractPtrs).kind == tyObject:
-    localError(n.info, "object contruction uses ':', not '='")
+    localError(c.config, n.info, "object contruction uses ':', not '='")
   var op = semExprWithType(c, n.sons[1])
   if targetType.isMetaType:
     let final = inferWithMetatype(c, targetType, op, true)
@@ -218,18 +227,18 @@ proc semConv(c: PContext, n: PNode): PNode =
       elif op.kind in {nkPar, nkTupleConstr} and targetType.kind == tyTuple:
         op = fitNode(c, targetType, op, result.info)
     of convNotNeedeed:
-      message(n.info, hintConvFromXtoItselfNotNeeded, result.typ.typeToString)
+      message(c.config, n.info, hintConvFromXtoItselfNotNeeded, result.typ.typeToString)
     of convNotLegal:
       result = fitNode(c, result.typ, result.sons[1], result.info)
       if result == nil:
-        localError(n.info, errGenerated, msgKindToString(errIllegalConvFromXtoY)%
+        localError(c.config, n.info, "illegal conversion from '$1' to '$2'" %
           [op.typ.typeToString, result.typ.typeToString])
   else:
     for i in countup(0, sonsLen(op) - 1):
       let it = op.sons[i]
       let status = checkConvertible(c, result.typ, it.typ)
       if status in {convOK, convNotNeedeed}:
-        markUsed(n.info, it.sym, c.graph.usageSym)
+        markUsed(c.config, n.info, it.sym, c.graph.usageSym)
         styleCheckUse(n.info, it.sym)
         markIndirect(c, it.sym)
         return it
@@ -237,16 +246,16 @@ proc semConv(c: PContext, n: PNode): PNode =
 
 proc semCast(c: PContext, n: PNode): PNode =
   ## Semantically analyze a casting ("cast[type](param)")
-  checkSonsLen(n, 2)
+  checkSonsLen(n, 2, c.config)
   let targetType = semTypeNode(c, n.sons[0], nil)
   let castedExpr = semExprWithType(c, n.sons[1])
   if tfHasMeta in targetType.flags:
-    localError(n.sons[0].info, errCastToANonConcreteType, $targetType)
+    localError(c.config, n.sons[0].info, "cannot cast to a non concrete type: '$1'" % $targetType)
   if not isCastable(targetType, castedExpr.typ):
     let tar = $targetType
     let alt = typeToString(targetType, preferDesc)
     let msg = if tar != alt: tar & "=" & alt else: tar
-    localError(n.info, errExprCannotBeCastToX, msg)
+    localError(c.config, n.info, "expression cannot be cast to " & msg)
   result = newNodeI(nkCast, n.info)
   result.typ = targetType
   addSon(result, copyTree(n.sons[0]))
@@ -256,13 +265,13 @@ proc semLowHigh(c: PContext, n: PNode, m: TMagic): PNode =
   const
     opToStr: array[mLow..mHigh, string] = ["low", "high"]
   if sonsLen(n) != 2:
-    localError(n.info, errXExpectsTypeOrValue, opToStr[m])
+    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})
     case typ.kind
     of tySequence, tyString, tyCString, tyOpenArray, tyVarargs:
-      n.typ = getSysType(tyInt)
+      n.typ = getSysType(c.graph, n.info, tyInt)
     of tyArray:
       n.typ = typ.sons[0] # indextype
     of tyInt..tyInt64, tyChar, tyBool, tyEnum, tyUInt8, tyUInt16, tyUInt32:
@@ -274,20 +283,20 @@ proc semLowHigh(c: PContext, n: PNode, m: TMagic): PNode =
       # that could easily turn into an infinite recursion in semtypinst
       n.typ = makeTypeFromExpr(c, n.copyTree)
     else:
-      localError(n.info, errInvalidArgForX, opToStr[m])
+      localError(c.config, n.info, "invalid argument for: " & opToStr[m])
   result = n
 
 proc semSizeof(c: PContext, n: PNode): PNode =
   if sonsLen(n) != 2:
-    localError(n.info, errXExpectsTypeOrValue, "sizeof")
+    localError(c.config, n.info, errXExpectsTypeOrValue % "sizeof")
   else:
     n.sons[1] = semExprWithType(c, n.sons[1], {efDetermineType})
     #restoreOldStyleType(n.sons[1])
-  n.typ = getSysType(tyInt)
+  n.typ = getSysType(c.graph, n.info, tyInt)
   result = n
 
 proc isOpImpl(c: PContext, n: PNode, flags: TExprFlags): PNode =
-  internalAssert n.sonsLen == 3 and
+  internalAssert c.config, n.sonsLen == 3 and
     n[1].typ != nil and n[1].typ.kind == tyTypeDesc and
     n[2].kind in {nkStrLit..nkTripleStrLit, nkType}
 
@@ -308,7 +317,9 @@ proc isOpImpl(c: PContext, n: PNode, flags: TExprFlags): PNode =
     maybeLiftType(t2, c, n.info)
     var m: TCandidate
     initCandidate(c, m, t2)
-    if efExplain in flags: m.diagnostics = @[]
+    if efExplain in flags:
+      m.diagnostics = @[]
+      m.diagnosticsEnabled = true
     let match = typeRel(m, t2, t1) >= isSubtype # isNone
     result = newIntNode(nkIntLit, ord(match))
 
@@ -316,10 +327,10 @@ proc isOpImpl(c: PContext, n: PNode, flags: TExprFlags): PNode =
 
 proc semIs(c: PContext, n: PNode, flags: TExprFlags): PNode =
   if sonsLen(n) != 3:
-    localError(n.info, errXExpectsTwoArguments, "is")
+    localError(c.config, n.info, "'is' operator takes 2 arguments")
 
   result = n
-  n.typ = getSysType(tyBool)
+  n.typ = getSysType(c.graph, n.info, tyBool)
 
   n.sons[1] = semExprWithType(c, n[1], {efDetermineType, efWantIterator})
   if n[2].kind notin {nkStrLit..nkTripleStrLit}:
@@ -341,8 +352,8 @@ proc semOpAux(c: PContext, n: PNode) =
   for i in countup(1, n.sonsLen-1):
     var a = n.sons[i]
     if a.kind == nkExprEqExpr and sonsLen(a) == 2:
-      var info = a.sons[0].info
-      a.sons[0] = newIdentNode(considerQuotedIdent(a.sons[0], a), info)
+      let info = a.sons[0].info
+      a.sons[0] = newIdentNode(considerQuotedIdent(c.config, a.sons[0], a), info)
       a.sons[1] = semExprWithType(c, a.sons[1], flags)
       a.typ = a.sons[1].typ
     else:
@@ -359,34 +370,34 @@ proc overloadedCallOpr(c: PContext, n: PNode): PNode =
     for i in countup(0, sonsLen(n) - 1): addSon(result, n.sons[i])
     result = semExpr(c, result)
 
-proc changeType(n: PNode, newType: PType, check: bool) =
+proc changeType(c: PContext; n: PNode, newType: PType, check: bool) =
   case n.kind
   of nkCurly, nkBracket:
     for i in countup(0, sonsLen(n) - 1):
-      changeType(n.sons[i], elemType(newType), check)
+      changeType(c, n.sons[i], elemType(newType), check)
   of nkPar, nkTupleConstr:
     let tup = newType.skipTypes({tyGenericInst, tyAlias, tySink})
     if tup.kind != tyTuple:
       if tup.kind == tyObject: return
-      globalError(n.info, "no tuple type for constructor")
+      globalError(c.config, n.info, "no tuple type for constructor")
     elif sonsLen(n) > 0 and n.sons[0].kind == nkExprColonExpr:
       # named tuple?
       for i in countup(0, sonsLen(n) - 1):
         var m = n.sons[i].sons[0]
         if m.kind != nkSym:
-          globalError(m.info, "invalid tuple constructor")
+          globalError(c.config, m.info, "invalid tuple constructor")
           return
         if tup.n != nil:
           var f = getSymFromList(tup.n, m.sym.name)
           if f == nil:
-            globalError(m.info, "unknown identifier: " & m.sym.name.s)
+            globalError(c.config, m.info, "unknown identifier: " & m.sym.name.s)
             return
-          changeType(n.sons[i].sons[1], f.typ, check)
+          changeType(c, n.sons[i].sons[1], f.typ, check)
         else:
-          changeType(n.sons[i].sons[1], tup.sons[i], check)
+          changeType(c, n.sons[i].sons[1], tup.sons[i], check)
     else:
       for i in countup(0, sonsLen(n) - 1):
-        changeType(n.sons[i], tup.sons[i], check)
+        changeType(c, n.sons[i], tup.sons[i], check)
         when false:
           var m = n.sons[i]
           var a = newNodeIT(nkExprColonExpr, m.info, newType.sons[i])
@@ -397,7 +408,7 @@ proc changeType(n: PNode, newType: PType, check: bool) =
     if check and n.kind != nkUInt64Lit:
       let value = n.intVal
       if value < firstOrd(newType) or value > lastOrd(newType):
-        localError(n.info, errGenerated, "cannot convert " & $value &
+        localError(c.config, n.info, "cannot convert " & $value &
                                          " to " & typeToString(newType))
   else: discard
   n.typ = newType
@@ -422,7 +433,7 @@ proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
   else:
     var x = n.sons[0]
     var lastIndex: BiggestInt = 0
-    var indexType = getSysType(tyInt)
+    var indexType = getSysType(c.graph, n.info, tyInt)
     if x.kind == nkExprColonExpr and sonsLen(x) == 2:
       var idx = semConstExpr(c, x.sons[0])
       lastIndex = getOrdValue(idx)
@@ -439,7 +450,7 @@ proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
         var idx = semConstExpr(c, x.sons[0])
         idx = fitNode(c, indexType, idx, x.info)
         if lastIndex+1 != getOrdValue(idx):
-          localError(x.info, errInvalidOrderInArrayConstructor)
+          localError(c.config, x.info, "invalid order in array constructor")
         x = x.sons[1]
 
       let xx = semExprWithType(c, x, flags*{efAllowDestructor})
@@ -463,22 +474,22 @@ proc fixAbstractType(c: PContext, n: PNode) =
             {tyNil, tyTuple, tySet} or it[1].isArrayConstr:
         var s = skipTypes(it.typ, abstractVar)
         if s.kind != tyExpr:
-          changeType(it.sons[1], s, check=true)
+          changeType(c, it.sons[1], s, check=true)
         n.sons[i] = it.sons[1]
 
 proc isAssignable(c: PContext, n: PNode; isUnsafeAddr=false): TAssignableResult =
   result = parampatterns.isAssignable(c.p.owner, n, isUnsafeAddr)
 
 proc newHiddenAddrTaken(c: PContext, n: PNode): PNode =
-  if n.kind == nkHiddenDeref and not (gCmd == cmdCompileToCpp or
+  if n.kind == nkHiddenDeref and not (c.config.cmd == cmdCompileToCpp or
                                       sfCompileToCpp in c.module.flags):
-    checkSonsLen(n, 1)
+    checkSonsLen(n, 1, c.config)
     result = n.sons[0]
   else:
     result = newNodeIT(nkHiddenAddr, n.info, makeVarType(c, n.typ))
     addSon(result, n)
     if isAssignable(c, n) notin {arLValue, arLocalLValue}:
-      localError(n.info, errVarForOutParamNeededX, renderNotLValue(n))
+      localError(c.config, n.info, errVarForOutParamNeededX % renderNotLValue(n))
 
 proc analyseIfAddressTaken(c: PContext, n: PNode): PNode =
   result = n
@@ -490,15 +501,15 @@ proc analyseIfAddressTaken(c: PContext, n: PNode): PNode =
       incl(n.sym.flags, sfAddrTaken)
       result = newHiddenAddrTaken(c, n)
   of nkDotExpr:
-    checkSonsLen(n, 2)
+    checkSonsLen(n, 2, c.config)
     if n.sons[1].kind != nkSym:
-      internalError(n.info, "analyseIfAddressTaken")
+      internalError(c.config, n.info, "analyseIfAddressTaken")
       return
     if skipTypes(n.sons[1].sym.typ, abstractInst-{tyTypeDesc}).kind notin {tyVar, tyLent}:
       incl(n.sons[1].sym.flags, sfAddrTaken)
       result = newHiddenAddrTaken(c, n)
   of nkBracketExpr:
-    checkMinSonsLen(n, 1)
+    checkMinSonsLen(n, 1, c.config)
     if skipTypes(n.sons[0].typ, abstractInst-{tyTypeDesc}).kind notin {tyVar, tyLent}:
       if n.sons[0].kind == nkSym: incl(n.sons[0].sym.flags, sfAddrTaken)
       result = newHiddenAddrTaken(c, n)
@@ -506,7 +517,7 @@ proc analyseIfAddressTaken(c: PContext, n: PNode): PNode =
     result = newHiddenAddrTaken(c, n)
 
 proc analyseIfAddressTakenInCall(c: PContext, n: PNode) =
-  checkMinSonsLen(n, 1)
+  checkMinSonsLen(n, 1, c.config)
   const
     FakeVarParams = {mNew, mNewFinalize, mInc, ast.mDec, mIncl, mExcl,
       mSetLengthStr, mSetLengthSeq, mAppendStrCh, mAppendStrStr, mSwap,
@@ -525,14 +536,21 @@ proc analyseIfAddressTakenInCall(c: PContext, n: PNode) =
         let it = n[i]
         if isAssignable(c, it) notin {arLValue, arLocalLValue}:
           if it.kind != nkHiddenAddr:
-            localError(it.info, errVarForOutParamNeededX, $it)
+            localError(c.config, it.info, errVarForOutParamNeededX % $it)
+    # bug #5113: disallow newSeq(result) where result is a 'var T':
+    if n[0].sym.magic in {mNew, mNewFinalize, mNewSeq}:
+      var arg = n[1] #.skipAddr
+      if arg.kind == nkHiddenDeref: arg = arg[0]
+      if arg.kind == nkSym and arg.sym.kind == skResult and
+          arg.typ.skipTypes(abstractInst).kind in {tyVar, tyLent}:
+        localError(c.config, n.info, errXStackEscape % renderTree(n[1], {renderNoComments}))
+
     return
   for i in countup(1, sonsLen(n) - 1):
     if n.sons[i].kind == nkHiddenCallConv:
       # we need to recurse explicitly here as converters can create nested
       # calls and then they wouldn't be analysed otherwise
       analyseIfAddressTakenInCall(c, n.sons[i])
-    semProcvarCheck(c, n.sons[i])
     if i < sonsLen(t) and
         skipTypes(t.sons[i], abstractInst-{tyTypeDesc}).kind == tyVar:
       if n.sons[i].kind != nkHiddenAddr:
@@ -554,14 +572,14 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode =
     call.add(n.sons[0])
     var allConst = true
     for i in 1 ..< n.len:
-      var a = getConstExpr(c.module, n.sons[i])
+      var a = getConstExpr(c.module, n.sons[i], c.graph)
       if a == nil:
         allConst = false
         a = n.sons[i]
         if a.kind == nkHiddenStdConv: a = a.sons[1]
       call.add(a)
     if allConst:
-      result = semfold.getConstExpr(c.module, call)
+      result = semfold.getConstExpr(c.module, call, c.graph)
       if result.isNil: result = n
       else: return result
 
@@ -583,7 +601,7 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode =
   if {sfNoSideEffect, sfCompileTime} * callee.flags != {} and
      {sfForward, sfImportc} * callee.flags == {} and n.typ != nil:
     if sfCompileTime notin callee.flags and
-        optImplicitStatic notin gOptions: return
+        optImplicitStatic notin c.config.options: return
 
     if callee.magic notin ctfeWhitelist: return
     if callee.kind notin {skProc, skFunc, skConverter} or callee.isGenericRoutine:
@@ -594,17 +612,17 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode =
     var call = newNodeIT(nkCall, n.info, n.typ)
     call.add(n.sons[0])
     for i in 1 ..< n.len:
-      let a = getConstExpr(c.module, n.sons[i])
+      let a = getConstExpr(c.module, n.sons[i], c.graph)
       if a == nil: return n
       call.add(a)
     #echo "NOW evaluating at compile time: ", call.renderTree
     if sfCompileTime in callee.flags:
-      result = evalStaticExpr(c.module, c.cache, call, c.p.owner)
+      result = evalStaticExpr(c.module, c.cache, c.graph, call, c.p.owner)
       if result.isNil:
-        localError(n.info, errCannotInterpretNodeX, renderTree(call))
+        localError(c.config, n.info, errCannotInterpretNodeX % renderTree(call))
       else: result = fixupTypeAfterEval(c, result, n)
     else:
-      result = evalConstExpr(c.module, c.cache, call)
+      result = evalConstExpr(c.module, c.cache, c.graph, call)
       if result.isNil: result = n
       else: result = fixupTypeAfterEval(c, result, n)
     #if result != n:
@@ -613,9 +631,9 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode =
 proc semStaticExpr(c: PContext, n: PNode): PNode =
   let a = semExpr(c, n.sons[0])
   if a.findUnresolvedStatic != nil: return a
-  result = evalStaticExpr(c.module, c.cache, a, c.p.owner)
+  result = evalStaticExpr(c.module, c.cache, c.graph, a, c.p.owner)
   if result.isNil:
-    localError(n.info, errCannotInterpretNodeX, renderTree(n))
+    localError(c.config, n.info, errCannotInterpretNodeX % renderTree(n))
     result = emptyNode
   else:
     result = fixupTypeAfterEval(c, result, a)
@@ -635,14 +653,14 @@ proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode,
 
   if result != nil:
     if result.sons[0].kind != nkSym:
-      internalError("semOverloadedCallAnalyseEffects")
+      internalError(c.config, "semOverloadedCallAnalyseEffects")
       return
     let callee = result.sons[0].sym
     case callee.kind
     of skMacro, skTemplate: discard
     else:
       if callee.kind == skIterator and callee.id == c.p.owner.id:
-        localError(n.info, errRecursiveDependencyX, callee.name.s)
+        localError(c.config, n.info, errRecursiveDependencyX % callee.name.s)
         # error correction, prevents endless for loop elimination in transf.
         # See bug #2051:
         result.sons[0] = newSymNode(errorSym(c, n))
@@ -655,7 +673,7 @@ proc resolveIndirectCall(c: PContext; n, nOrig: PNode;
   matches(c, n, nOrig, result)
   if result.state != csMatch:
     # try to deref the first argument:
-    if experimentalMode(c) and canDeref(n):
+    if implicitDeref in c.features and canDeref(n):
       n.sons[1] = n.sons[1].tryDeref
       initCandidate(c, result, t)
       matches(c, n, nOrig, result)
@@ -690,10 +708,10 @@ proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags): PNode =
 
 proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
   result = nil
-  checkMinSonsLen(n, 1)
+  checkMinSonsLen(n, 1, c.config)
   var prc = n.sons[0]
   if n.sons[0].kind == nkDotExpr:
-    checkSonsLen(n.sons[0], 2)
+    checkSonsLen(n.sons[0], 2, c.config)
     let n0 = semFieldAccess(c, n.sons[0])
     if n0.kind == nkDotCall:
       # it is a static call!
@@ -726,11 +744,11 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
     if m.state != csMatch:
       if errorOutputs == {}:
         # speed up error generation:
-        globalError(n.info, errTypeMismatch, "")
+        globalError(c.config, n.info, "type mismatch")
         return emptyNode
       else:
         var hasErrorType = false
-        var msg = msgKindToString(errTypeMismatch)
+        var msg = "type mismatch: got <"
         for i in countup(1, sonsLen(n) - 1):
           if i > 1: add(msg, ", ")
           let nt = n.sons[i].typ
@@ -739,9 +757,9 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
             hasErrorType = true
             break
         if not hasErrorType:
-          add(msg, ">\n" & msgKindToString(errButExpected) & "\n" &
+          add(msg, ">\nbut expected one of: \n" &
               typeToString(n.sons[0].typ))
-          localError(n.info, errGenerated, msg)
+          localError(c.config, n.info, msg)
         return errorNode(c, n)
       result = nil
     else:
@@ -784,11 +802,11 @@ 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(magicsys.systemModule.tab, getIdent"echo")
+  var e = strTableGet(c.graph.systemModule.tab, getIdent"echo")
   if e != nil:
     add(result, newSymNode(e))
   else:
-    localError(n.info, errSystemNeeds, "echo")
+    localError(c.config, n.info, "system needs: echo")
     add(result, errorNode(c, n))
   add(result, n)
   result = semExpr(c, result)
@@ -832,8 +850,8 @@ proc lookupInRecordAndBuildCheck(c: PContext, n, r: PNode, field: PIdent,
       result = lookupInRecordAndBuildCheck(c, n, r.sons[i], field, check)
       if result != nil: return
   of nkRecCase:
-    checkMinSonsLen(r, 2)
-    if (r.sons[0].kind != nkSym): illFormedAst(r)
+    checkMinSonsLen(r, 2, c.config)
+    if (r.sons[0].kind != nkSym): illFormedAst(r, c.config)
     result = lookupInRecordAndBuildCheck(c, n, r.sons[0], field, check)
     if result != nil: return
     let setType = createSetType(c, r.sons[0].typ)
@@ -851,8 +869,8 @@ proc lookupInRecordAndBuildCheck(c: PContext, n, r: PNode, field: PIdent,
             addSon(check, ast.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(tyBool))
-          addSon(inExpr, newSymNode(opContains, n.info))
+          var inExpr = newNodeIT(nkCall, n.info, getSysType(c.graph, n.info, tyBool))
+          addSon(inExpr, newSymNode(c.graph.opContains, n.info))
           addSon(inExpr, s)
           addSon(inExpr, copyTree(r.sons[0]))
           addSon(check, inExpr)
@@ -864,19 +882,19 @@ proc lookupInRecordAndBuildCheck(c: PContext, n, r: PNode, field: PIdent,
           if check == nil:
             check = newNodeI(nkCheckedFieldExpr, n.info)
             addSon(check, ast.emptyNode) # make space for access node
-          var inExpr = newNodeIT(nkCall, n.info, getSysType(tyBool))
-          addSon(inExpr, newSymNode(opContains, n.info))
+          var inExpr = newNodeIT(nkCall, n.info, getSysType(c.graph, n.info, tyBool))
+          addSon(inExpr, newSymNode(c.graph.opContains, n.info))
           addSon(inExpr, s)
           addSon(inExpr, copyTree(r.sons[0]))
-          var notExpr = newNodeIT(nkCall, n.info, getSysType(tyBool))
-          addSon(notExpr, newSymNode(opNot, n.info))
+          var notExpr = newNodeIT(nkCall, n.info, getSysType(c.graph, n.info, tyBool))
+          addSon(notExpr, newSymNode(c.graph.opNot, n.info))
           addSon(notExpr, inExpr)
           addSon(check, notExpr)
           return
-      else: illFormedAst(it)
+      else: illFormedAst(it, c.config)
   of nkSym:
     if r.sym.name.id == field.id: result = r.sym
-  else: illFormedAst(n)
+  else: illFormedAst(n, c.config)
 
 const
   tyTypeParamsHolders = {tyGenericInst, tyCompositeTypeClass}
@@ -931,12 +949,12 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
   let s = getGenSym(c, sym)
   case s.kind
   of skConst:
-    markUsed(n.info, s, c.graph.usageSym)
+    markUsed(c.config, n.info, s, c.graph.usageSym)
     styleCheckUse(n.info, s)
     case skipTypes(s.typ, abstractInst-{tyTypeDesc}).kind
     of  tyNil, tyChar, tyInt..tyInt64, tyFloat..tyFloat128,
         tyTuple, tySet, tyUInt..tyUInt64:
-      if s.magic == mNone: result = inlineConst(n, s)
+      if s.magic == mNone: result = inlineConst(c, n, s)
       else: result = newSymNode(s, n.info)
     of tyArray, tySequence:
       # Consider::
@@ -949,27 +967,29 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
       # It is clear that ``[]`` means two totally different things. Thus, we
       # copy `x`'s AST into each context, so that the type fixup phase can
       # deal with two different ``[]``.
-      if s.ast.len == 0: result = inlineConst(n, s)
+      if s.ast.len == 0: result = inlineConst(c, n, s)
       else: result = newSymNode(s, n.info)
     else:
       result = newSymNode(s, n.info)
   of skMacro:
-    if efNoEvaluateGeneric in flags and s.ast[genericParamsPos].len > 0:
-      markUsed(n.info, s, c.graph.usageSym)
+    if efNoEvaluateGeneric in flags and s.ast[genericParamsPos].len > 0 or
+       (n.kind notin nkCallKinds and s.requiredParams > 0):
+      markUsed(c.config, n.info, s, c.graph.usageSym)
       styleCheckUse(n.info, s)
-      result = newSymNode(s, n.info)
+      result = symChoice(c, n, s, scClosed)
     else:
       result = semMacroExpr(c, n, n, s, flags)
   of skTemplate:
     if efNoEvaluateGeneric in flags and s.ast[genericParamsPos].len > 0 or
+       (n.kind notin nkCallKinds and s.requiredParams > 0) or
        sfCustomPragma in sym.flags:
-      markUsed(n.info, s, c.graph.usageSym)
+      markUsed(c.config, n.info, s, c.graph.usageSym)
       styleCheckUse(n.info, s)
-      result = newSymNode(s, n.info)
+      result = symChoice(c, n, s, scClosed)
     else:
       result = semTemplateExpr(c, n, s, flags)
   of skParam:
-    markUsed(n.info, s, c.graph.usageSym)
+    markUsed(c.config, n.info, s, c.graph.usageSym)
     styleCheckUse(n.info, s)
     if s.typ != nil and s.typ.kind == tyStatic and s.typ.n != nil:
       # XXX see the hack in sigmatch.nim ...
@@ -979,19 +999,19 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
         # gensym'ed parameters that nevertheless have been forward declared
         # need a special fixup:
         let realParam = c.p.owner.typ.n[s.position+1]
-        internalAssert realParam.kind == nkSym and realParam.sym.kind == skParam
+        internalAssert c.config, realParam.kind == nkSym and realParam.sym.kind == skParam
         return newSymNode(c.p.owner.typ.n[s.position+1].sym, n.info)
       elif c.p.owner.kind == skMacro:
         # gensym'ed macro parameters need a similar hack (see bug #1944):
         var u = searchInScopes(c, s.name)
-        internalAssert u != nil and u.kind == skParam and u.owner == s.owner
+        internalAssert c.config, u != nil and u.kind == skParam and u.owner == s.owner
         return newSymNode(u, n.info)
     result = newSymNode(s, n.info)
   of skVar, skLet, skResult, skForVar:
     if s.magic == mNimvm:
-      localError(n.info, "illegal context for 'nimvm' magic")
+      localError(c.config, n.info, "illegal context for 'nimvm' magic")
 
-    markUsed(n.info, s, c.graph.usageSym)
+    markUsed(c.config, n.info, s, c.graph.usageSym)
     styleCheckUse(n.info, s)
     result = newSymNode(s, n.info)
     # We cannot check for access to outer vars for example because it's still
@@ -1009,7 +1029,7 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
       n.typ = s.typ
       return n
   of skType:
-    markUsed(n.info, s, c.graph.usageSym)
+    markUsed(c.config, n.info, s, c.graph.usageSym)
     styleCheckUse(n.info, s)
     if s.typ.kind == tyStatic and s.typ.n != nil:
       return s.typ.n
@@ -1031,7 +1051,7 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
           if f != nil and fieldVisible(c, f):
             # is the access to a public field or in the same module or in a friend?
             doAssert f == s
-            markUsed(n.info, f, c.graph.usageSym)
+            markUsed(c.config, n.info, f, c.graph.usageSym)
             styleCheckUse(n.info, f)
             result = newNodeIT(nkDotExpr, n.info, f.typ)
             result.add makeDeref(newSymNode(p.selfSym))
@@ -1044,23 +1064,23 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
           if ty.sons[0] == nil: break
           ty = skipTypes(ty.sons[0], skipPtrs)
     # old code, not sure if it's live code:
-    markUsed(n.info, s, c.graph.usageSym)
+    markUsed(c.config, n.info, s, c.graph.usageSym)
     styleCheckUse(n.info, s)
     result = newSymNode(s, n.info)
   else:
-    markUsed(n.info, s, c.graph.usageSym)
+    markUsed(c.config, n.info, s, c.graph.usageSym)
     styleCheckUse(n.info, s)
     result = newSymNode(s, n.info)
 
 proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
   ## returns nil if it's not a built-in field access
-  checkSonsLen(n, 2)
+  checkSonsLen(n, 2, c.config)
   # tests/bind/tbindoverload.nim wants an early exit here, but seems to
   # work without now. template/tsymchoicefield doesn't like an early exit
   # here at all!
   #if isSymChoice(n.sons[1]): return
   when defined(nimsuggest):
-    if gCmd == cmdIdeTools:
+    if c.config.cmd == cmdIdeTools:
       suggestExpr(c, n)
       if exactEquals(gTrackPos, n[1].info): suggestExprNoCheck(c, n)
 
@@ -1070,14 +1090,14 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
       result = symChoice(c, n, s, scClosed)
       if result.kind == nkSym: result = semSym(c, n, s, flags)
     else:
-      markUsed(n.sons[1].info, s, c.graph.usageSym)
+      markUsed(c.config, n.sons[1].info, s, c.graph.usageSym)
       result = semSym(c, n, s, flags)
     styleCheckUse(n.sons[1].info, s)
     return
 
   n.sons[0] = semExprWithType(c, n.sons[0], flags+{efDetermineType})
   #restoreOldStyleType(n.sons[0])
-  var i = considerQuotedIdent(n.sons[1], n)
+  var i = considerQuotedIdent(c.config, n.sons[1], n)
   var ty = n.sons[0].typ
   var f: PSym = nil
   result = nil
@@ -1134,7 +1154,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
         result = newSymNode(f)
         result.info = n.info
         result.typ = ty
-        markUsed(n.info, f, c.graph.usageSym)
+        markUsed(c.config, n.info, f, c.graph.usageSym)
         styleCheckUse(n.info, f)
         return
     of tyObject, tyTuple:
@@ -1165,7 +1185,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
     if f != nil:
       if fieldVisible(c, f):
         # is the access to a public field or in the same module or in a friend?
-        markUsed(n.sons[1].info, f, c.graph.usageSym)
+        markUsed(c.config, n.sons[1].info, f, c.graph.usageSym)
         styleCheckUse(n.sons[1].info, f)
         n.sons[0] = makeDeref(n.sons[0])
         n.sons[1] = newSymNode(f) # we now have the correct field
@@ -1179,7 +1199,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
   elif ty.kind == tyTuple and ty.n != nil:
     f = getSymFromList(ty.n, i)
     if f != nil:
-      markUsed(n.sons[1].info, f, c.graph.usageSym)
+      markUsed(c.config, n.sons[1].info, f, c.graph.usageSym)
       styleCheckUse(n.sons[1].info, f)
       n.sons[0] = makeDeref(n.sons[0])
       n.sons[1] = newSymNode(f)
@@ -1197,7 +1217,7 @@ proc dotTransformation(c: PContext, n: PNode): PNode =
     addSon(result, n.sons[1])
     addSon(result, copyTree(n[0]))
   else:
-    var i = considerQuotedIdent(n.sons[1], n)
+    var i = considerQuotedIdent(c.config, n.sons[1], n)
     result = newNodeI(nkDotCall, n.info)
     result.flags.incl nfDotField
     addSon(result, newIdentNode(i, n[1].info))
@@ -1216,7 +1236,7 @@ proc buildOverloadedSubscripts(n: PNode, ident: PIdent): PNode =
   for i in 0 .. n.len-1: result.add(n[i])
 
 proc semDeref(c: PContext, n: PNode): PNode =
-  checkSonsLen(n, 1)
+  checkSonsLen(n, 1, c.config)
   n.sons[0] = semExprWithType(c, n.sons[0])
   result = n
   var t = skipTypes(n.sons[0].typ, {tyGenericInst, tyVar, tyLent, tyAlias, tySink})
@@ -1234,10 +1254,10 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
     result = newNodeIT(nkDerefExpr, x.info, x.typ)
     result.add(x[0])
     return
-  checkMinSonsLen(n, 2)
+  checkMinSonsLen(n, 2, c.config)
   # make sure we don't evaluate generic macros/templates
   n.sons[0] = semExprWithType(c, n.sons[0],
-                              {efNoProcvarCheck, efNoEvaluateGeneric})
+                              {efNoEvaluateGeneric})
   let arr = skipTypes(n.sons[0].typ, {tyGenericInst,
                                       tyVar, tyLent, tyPtr, tyRef, tyAlias, tySink})
   case arr.kind
@@ -1248,7 +1268,7 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
     for i in countup(1, sonsLen(n) - 1):
       n.sons[i] = semExprWithType(c, n.sons[i],
                                   flags*{efInTypeof, efDetermineType})
-    var indexType = if arr.kind == tyArray: arr.sons[0] else: getSysType(tyInt)
+    var indexType = if arr.kind == tyArray: arr.sons[0] else: getSysType(c.graph, n.info, tyInt)
     var arg = indexTypesMatch(c, indexType, n.sons[1].typ, n.sons[1])
     if arg != nil:
       n.sons[1] = arg
@@ -1271,7 +1291,7 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
         {tyInt..tyInt64}:
       var idx = getOrdValue(n.sons[1])
       if idx >= 0 and idx < sonsLen(arr): n.typ = arr.sons[int(idx)]
-      else: localError(n.info, errInvalidIndexValueForTuple)
+      else: localError(c.config, n.info, "invalid index value for tuple subscript")
       result = n
     else:
       result = nil
@@ -1311,7 +1331,7 @@ proc semArrayAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
     result = semExpr(c, buildOverloadedSubscripts(n, getIdent"[]"))
 
 proc propertyWriteAccess(c: PContext, n, nOrig, a: PNode): PNode =
-  var id = considerQuotedIdent(a[1], a)
+  var id = considerQuotedIdent(c.config, a[1], a)
   var setterId = newIdentNode(getIdent(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
@@ -1334,11 +1354,11 @@ proc takeImplicitAddr(c: PContext, n: PNode; isLent: bool): PNode =
   let root = exprRoot(n)
   if root != nil and root.owner == c.p.owner:
     if root.kind in {skLet, skVar, skTemp} and sfGlobal notin root.flags:
-      localError(n.info, "'$1' escapes its stack frame; context: '$2'" % [
-        root.name.s, renderTree(n, {renderNoComments})])
+      localError(c.config, n.info, "'$1' escapes its stack frame; context: '$2'; see $3/var_t_return.html" % [
+        root.name.s, renderTree(n, {renderNoComments}), explanationsBaseUrl])
     elif root.kind == skParam and root.position != 0:
-      localError(n.info, "'$1' is not the first parameter; context: '$2'" % [
-        root.name.s, renderTree(n, {renderNoComments})])
+      localError(c.config, n.info, "'$1' is not the first parameter; context: '$2'; see $3/var_t_return.html" % [
+        root.name.s, renderTree(n, {renderNoComments}), explanationsBaseUrl])
   case n.kind
   of nkHiddenAddr, nkAddr: return n
   of nkHiddenDeref, nkDerefExpr: return n.sons[0]
@@ -1348,9 +1368,9 @@ proc takeImplicitAddr(c: PContext, n: PNode; isLent: bool): PNode =
   let valid = isAssignable(c, n)
   if valid != arLValue:
     if valid == arLocalLValue:
-      localError(n.info, errXStackEscape, renderTree(n, {renderNoComments}))
+      localError(c.config, n.info, errXStackEscape % renderTree(n, {renderNoComments}))
     elif not isLent:
-      localError(n.info, errExprHasNoAddress)
+      localError(c.config, n.info, errExprHasNoAddress)
   result = newNodeIT(nkHiddenAddr, n.info, makePtrType(c, n.typ))
   result.add(n)
 
@@ -1367,7 +1387,7 @@ template resultTypeIsInferrable(typ: PType): untyped =
   typ.isMetaType and typ.kind != tyTypeDesc
 
 proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
-  checkSonsLen(n, 2)
+  checkSonsLen(n, 2, c.config)
   var a = n.sons[0]
   case a.kind
   of nkDotExpr:
@@ -1420,7 +1440,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
         isAssignable(c, a) == arNone) or
       skipTypes(le, abstractVar).kind in {tyOpenArray, tyVarargs}:
     # Direct assignment to a discriminant is allowed!
-    localError(a.info, errXCannotBeAssignedTo,
+    localError(c.config, a.info, errXCannotBeAssignedTo %
                renderTree(a, {renderNoComments}))
   else:
     let
@@ -1436,15 +1456,15 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
         if rhsTyp.kind in tyUserTypeClasses and rhsTyp.isResolvedUserTypeClass:
           rhsTyp = rhsTyp.lastSon
         if cmpTypes(c, lhs.typ, rhsTyp) in {isGeneric, isEqual}:
-          internalAssert c.p.resultSym != nil
+          internalAssert c.config, c.p.resultSym != nil
           lhs.typ = rhsTyp
           c.p.resultSym.typ = rhsTyp
           c.p.owner.typ.sons[0] = rhsTyp
         else:
-          typeMismatch(n.info, lhs.typ, rhsTyp)
+          typeMismatch(c.config, n.info, lhs.typ, rhsTyp)
 
     n.sons[1] = fitNode(c, le, rhs, n.info)
-    if not newDestructors:
+    if destructor notin c.features:
       if tfHasAsgn in lhs.typ.flags and not lhsIsResult and
           mode != noOverloadedAsgn:
         return overloadedAsgn(c, lhs, n.sons[1])
@@ -1457,7 +1477,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
 
 proc semReturn(c: PContext, n: PNode): PNode =
   result = n
-  checkSonsLen(n, 1)
+  checkSonsLen(n, 1, c.config)
   if c.p.owner.kind in {skConverter, skMethod, skProc, skFunc, skMacro} or (
      c.p.owner.kind == skIterator and c.p.owner.typ.callConv == ccClosure):
     if n.sons[0].kind != nkEmpty:
@@ -1471,9 +1491,9 @@ proc semReturn(c: PContext, n: PNode): PNode =
         if n[0][1].kind == nkSym and n[0][1].sym == c.p.resultSym:
           n.sons[0] = ast.emptyNode
       else:
-        localError(n.info, errNoReturnTypeDeclared)
+        localError(c.config, n.info, errNoReturnTypeDeclared)
   else:
-    localError(n.info, errXNotAllowedHere, "\'return\'")
+    localError(c.config, n.info, "'return' not allowed here")
 
 proc semProcBody(c: PContext, n: PNode): PNode =
   openScope(c)
@@ -1488,7 +1508,7 @@ proc semProcBody(c: PContext, n: PNode): PNode =
       #   nil
       #   # comment
       # are not expressions:
-      fixNilType(result)
+      fixNilType(c, result)
     else:
       var a = newNodeI(nkAsgn, n.info, 2)
       a.sons[0] = newSymNode(c.p.resultSym)
@@ -1504,7 +1524,7 @@ proc semProcBody(c: PContext, n: PNode): PNode =
       c.p.resultSym.typ = errorType(c)
       c.p.owner.typ.sons[0] = nil
     else:
-      localError(c.p.resultSym.info, errCannotInferReturnType)
+      localError(c.config, c.p.resultSym.info, errCannotInferReturnType)
 
   closeScope(c)
 
@@ -1528,16 +1548,16 @@ proc semYieldVarResult(c: PContext, n: PNode, restype: PType) =
           var a = n.sons[0].sons[1]
           a.sons[i] = takeImplicitAddr(c, a.sons[i], false)
         else:
-          localError(n.sons[0].info, errXExpected, "tuple constructor")
+          localError(c.config, n.sons[0].info, errXExpected, "tuple constructor")
   else: discard
 
 proc semYield(c: PContext, n: PNode): PNode =
   result = n
-  checkSonsLen(n, 1)
+  checkSonsLen(n, 1, c.config)
   if c.p.owner == nil or c.p.owner.kind != skIterator:
-    localError(n.info, errYieldNotAllowedHere)
+    localError(c.config, n.info, errYieldNotAllowedHere)
   elif c.p.inTryStmt > 0 and c.p.owner.typ.callConv != ccInline:
-    localError(n.info, errYieldNotAllowedInTryStmt)
+    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
@@ -1545,7 +1565,7 @@ proc semYield(c: PContext, n: PNode): PNode =
     if restype != nil:
       if restype.kind != tyExpr:
         n.sons[0] = fitNode(c, restype, n.sons[0], n.info)
-      if n.sons[0].typ == nil: internalError(n.info, "semYield")
+      if n.sons[0].typ == nil: internalError(c.config, n.info, "semYield")
 
       if resultTypeIsInferrable(restype):
         let inferred = n.sons[0].typ
@@ -1553,9 +1573,9 @@ proc semYield(c: PContext, n: PNode): PNode =
 
       semYieldVarResult(c, n, restype)
     else:
-      localError(n.info, errCannotReturnExpr)
+      localError(c.config, n.info, errCannotReturnExpr)
   elif c.p.owner.typ.sons[0] != nil:
-    localError(n.info, errGenerated, "yield statement must yield a value")
+    localError(c.config, n.info, errGenerated, "yield statement must yield a value")
 
 proc lookUpForDefined(c: PContext, i: PIdent, onlyCurrentScope: bool): PSym =
   if onlyCurrentScope:
@@ -1570,37 +1590,37 @@ proc lookUpForDefined(c: PContext, n: PNode, onlyCurrentScope: bool): PSym =
   of nkDotExpr:
     result = nil
     if onlyCurrentScope: return
-    checkSonsLen(n, 2)
+    checkSonsLen(n, 2, c.config)
     var m = lookUpForDefined(c, n.sons[0], onlyCurrentScope)
     if m != nil and m.kind == skModule:
-      let ident = considerQuotedIdent(n[1], n)
+      let ident = considerQuotedIdent(c.config, 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(n), onlyCurrentScope)
+    result = lookUpForDefined(c, considerQuotedIdent(c.config, n), onlyCurrentScope)
   of nkSym:
     result = n.sym
   of nkOpenSymChoice, nkClosedSymChoice:
     result = n.sons[0].sym
   else:
-    localError(n.info, errIdentifierExpected, renderTree(n))
+    localError(c.config, n.info, "identifier expected, but got: " & renderTree(n))
     result = nil
 
 proc semDefined(c: PContext, n: PNode, onlyCurrentScope: bool): PNode =
-  checkSonsLen(n, 2)
+  checkSonsLen(n, 2, c.config)
   # we replace this node by a 'true' or 'false' node:
   result = newIntNode(nkIntLit, 0)
-  if not onlyCurrentScope and considerQuotedIdent(n[0], n).s == "defined":
+  if not onlyCurrentScope and considerQuotedIdent(c.config, n[0], n).s == "defined":
     if n.sons[1].kind != nkIdent:
-      localError(n.info, "obsolete usage of 'defined', use 'declared' instead")
-    elif condsyms.isDefined(n.sons[1].ident):
+      localError(c.config, n.info, "obsolete usage of 'defined', use 'declared' instead")
+    elif isDefined(c.config, n.sons[1].ident.s):
       result.intVal = 1
   elif lookUpForDefined(c, n.sons[1], onlyCurrentScope) != nil:
     result.intVal = 1
   result.info = n.info
-  result.typ = getSysType(tyBool)
+  result.typ = getSysType(c.graph, n.info, tyBool)
 
 proc expectMacroOrTemplateCall(c: PContext, n: PNode): PSym =
   ## The argument to the proc should be nkCall(...) or similar
@@ -1612,12 +1632,12 @@ proc expectMacroOrTemplateCall(c: PContext, n: PNode): PSym =
       return errorSym(c, n[0])
 
     if expandedSym.kind notin {skMacro, skTemplate}:
-      localError(n.info, errXisNoMacroOrTemplate, expandedSym.name.s)
+      localError(c.config, n.info, "'$1' is not a macro or template" % expandedSym.name.s)
       return errorSym(c, n[0])
 
     result = expandedSym
   else:
-    localError(n.info, errXisNoMacroOrTemplate, n.renderTree)
+    localError(c.config, n.info, "'$1' is not a macro or template" % n.renderTree)
     result = errorSym(c, n)
 
 proc expectString(c: PContext, n: PNode): string =
@@ -1625,11 +1645,7 @@ proc expectString(c: PContext, n: PNode): string =
   if n.kind in nkStrKinds:
     return n.strVal
   else:
-    localError(n.info, errStringLiteralExpected)
-
-proc getMagicSym(magic: TMagic): PSym =
-  result = newSym(skProc, getIdent($magic), systemModule, gCodegenLineInfo)
-  result.magic = magic
+    localError(c.config, n.info, errStringLiteralExpected)
 
 proc newAnonSym(c: PContext; kind: TSymKind, info: TLineInfo): PSym =
   result = newSym(kind, c.cache.idAnon, getCurrOwner(c), info)
@@ -1643,7 +1659,7 @@ proc semExpandToAst(c: PContext, n: PNode): PNode =
     if expandedSym.kind == skError: return n
 
     macroCall.sons[0] = newSymNode(expandedSym, macroCall.info)
-    markUsed(n.info, expandedSym, c.graph.usageSym)
+    markUsed(c.config, n.info, expandedSym, c.graph.usageSym)
     styleCheckUse(n.info, expandedSym)
 
   if isCallExpr(macroCall):
@@ -1662,26 +1678,25 @@ proc semExpandToAst(c: PContext, n: PNode): PNode =
         inc cands
       symx = nextOverloadIter(o, c, headSymbol)
     if cands == 0:
-      localError(n.info, "expected a template that takes " & $(macroCall.len-1) & " arguments")
+      localError(c.config, n.info, "expected a template that takes " & $(macroCall.len-1) & " arguments")
     elif cands >= 2:
-      localError(n.info, "ambiguous symbol in 'getAst' context: " & $macroCall)
+      localError(c.config, n.info, "ambiguous symbol in 'getAst' context: " & $macroCall)
     else:
       let info = macroCall.sons[0].info
       macroCall.sons[0] = newSymNode(cand, info)
-      markUsed(info, cand, c.graph.usageSym)
+      markUsed(c.config, info, cand, c.graph.usageSym)
       styleCheckUse(info, cand)
 
     # we just perform overloading resolution here:
     #n.sons[1] = semOverloadedCall(c, macroCall, macroCall, {skTemplate, skMacro})
   else:
-    localError(n.info, "getAst takes a call, but got " & n.renderTree)
+    localError(c.config, n.info, "getAst takes a call, but got " & n.renderTree)
   # Preserve the magic symbol in order to be handled in evals.nim
-  internalAssert n.sons[0].sym.magic == mExpandToAst
+  internalAssert c.config, n.sons[0].sym.magic == mExpandToAst
   #n.typ = getSysSym("NimNode").typ # expandedSym.getReturnType
   if n.kind == nkStmtList and n.len == 1: result = n[0]
   else: result = n
-  result.typ = if getCompilerProc("NimNode") != nil: sysTypeFromName"NimNode"
-               else: sysTypeFromName"PNimrodNode"
+  result.typ = sysTypeFromName(c.graph, n.info, "NimNode")
 
 proc semExpandToAst(c: PContext, n: PNode, magicSym: PSym,
                     flags: TExprFlags = {}): PNode =
@@ -1691,7 +1706,7 @@ proc semExpandToAst(c: PContext, n: PNode, magicSym: PSym,
   else:
     result = semDirectOp(c, n, flags)
 
-proc processQuotations(n: var PNode, op: string,
+proc processQuotations(c: PContext; n: var PNode, op: string,
                        quotes: var seq[PNode],
                        ids: var seq[PNode]) =
   template returnQuote(q) =
@@ -1701,7 +1716,7 @@ proc processQuotations(n: var PNode, op: string,
     return
 
   if n.kind == nkPrefix:
-    checkSonsLen(n, 2)
+    checkSonsLen(n, 2, c.config)
     if n[0].kind == nkIdent:
       var examinedOp = n[0].ident.s
       if examinedOp == op:
@@ -1712,10 +1727,10 @@ proc processQuotations(n: var PNode, op: string,
     returnQuote n[0]
 
   for i in 0 ..< n.safeLen:
-    processQuotations(n.sons[i], op, quotes, ids)
+    processQuotations(c, n.sons[i], op, quotes, ids)
 
 proc semQuoteAst(c: PContext, n: PNode): PNode =
-  internalAssert n.len == 2 or n.len == 3
+  internalAssert c.config, n.len == 2 or n.len == 3
   # We transform the do block into a template with a param for
   # each interpolation. We'll pass this template to getAst.
   var
@@ -1728,9 +1743,9 @@ proc semQuoteAst(c: PContext, n: PNode): PNode =
       # this will store the generated param names
 
   if quotedBlock.kind != nkStmtList:
-    localError(n.info, errXExpected, "block")
+    localError(c.config, n.info, errXExpected, "block")
 
-  processQuotations(quotedBlock, op, quotes, ids)
+  processQuotations(c, quotedBlock, op, quotes, ids)
 
   var dummyTemplate = newProcNode(
     nkTemplateDef, quotedBlock.info, quotedBlock,
@@ -1738,27 +1753,27 @@ proc semQuoteAst(c: PContext, n: PNode): PNode =
 
   if ids.len > 0:
     dummyTemplate.sons[paramsPos] = newNodeI(nkFormalParams, n.info)
-    dummyTemplate[paramsPos].add getSysSym("typed").newSymNode # return type
-    ids.add getSysSym("untyped").newSymNode # params type
+    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
     dummyTemplate[paramsPos].add newNode(nkIdentDefs, n.info, ids)
 
   var tmpl = semTemplateDef(c, dummyTemplate)
   quotes[0] = tmpl[namePos]
   result = newNode(nkCall, n.info, @[
-    getMagicSym(mExpandToAst).newSymNode,
+     createMagic(c.graph, "getAst", mExpandToAst).newSymNode,
     newNode(nkCall, n.info, quotes)])
   result = semExpandToAst(c, result)
 
 proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
   # watch out, hacks ahead:
-  let oldErrorCount = msgs.gErrorCounter
-  let oldErrorMax = msgs.gErrorMax
+  let oldErrorCount = c.config.errorCounter
+  let oldErrorMax = c.config.errorMax
   let oldCompilesId = c.compilesContextId
   inc c.compilesContextIdGenerator
   c.compilesContextId = c.compilesContextIdGenerator
   # do not halt after first error:
-  msgs.gErrorMax = high(int)
+  c.config.errorMax = high(int)
 
   # open a scope for temporary symbol inclusions:
   let oldScope = c.currentScope
@@ -1772,12 +1787,13 @@ proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
   let oldInGenericContext = c.inGenericContext
   let oldInUnrolledContext = c.inUnrolledContext
   let oldInGenericInst = c.inGenericInst
+  let oldInStaticContext = c.inStaticContext
   let oldProcCon = c.p
   c.generics = @[]
   var err: string
   try:
     result = semExpr(c, n, flags)
-    if msgs.gErrorCounter != oldErrorCount: result = nil
+    if c.config.errorCounter != oldErrorCount: result = nil
   except ERecoverableError:
     discard
   # undo symbol table changes (as far as it's possible):
@@ -1786,13 +1802,14 @@ proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
   c.inGenericContext = oldInGenericContext
   c.inUnrolledContext = oldInUnrolledContext
   c.inGenericInst = oldInGenericInst
+  c.inStaticContext = oldInStaticContext
   c.p = oldProcCon
   msgs.setInfoContextLen(oldContextLen)
   setLen(c.graph.owners, oldOwnerLen)
   c.currentScope = oldScope
   errorOutputs = oldErrorOutputs
-  msgs.gErrorCounter = oldErrorCount
-  msgs.gErrorMax = oldErrorMax
+  c.config.errorCounter = oldErrorCount
+  c.config.errorMax = oldErrorMax
 
 proc semCompiles(c: PContext, n: PNode, flags: TExprFlags): PNode =
   # we replace this node by a 'true' or 'false' node:
@@ -1800,7 +1817,7 @@ proc semCompiles(c: PContext, n: PNode, flags: TExprFlags): PNode =
 
   result = newIntNode(nkIntLit, ord(tryExpr(c, n[1], flags) != nil))
   result.info = n.info
-  result.typ = getSysType(tyBool)
+  result.typ = getSysType(c.graph, n.info, tyBool)
 
 proc semShallowCopy(c: PContext, n: PNode, flags: TExprFlags): PNode =
   if sonsLen(n) == 3:
@@ -1815,15 +1832,15 @@ proc semShallowCopy(c: PContext, n: PNode, flags: TExprFlags): PNode =
 
 proc createFlowVar(c: PContext; t: PType; info: TLineInfo): PType =
   result = newType(tyGenericInvocation, c.module)
-  addSonSkipIntLit(result, magicsys.getCompilerProc("FlowVar").typ)
+  addSonSkipIntLit(result, magicsys.getCompilerProc(c.graph, "FlowVar").typ)
   addSonSkipIntLit(result, t)
   result = instGenericContainer(c, info, result, allowMetaTypes = false)
 
 proc instantiateCreateFlowVarCall(c: PContext; t: PType;
                                   info: TLineInfo): PSym =
-  let sym = magicsys.getCompilerProc("nimCreateFlowVar")
+  let sym = magicsys.getCompilerProc(c.graph, "nimCreateFlowVar")
   if sym == nil:
-    localError(info, errSystemNeeds, "nimCreateFlowVar")
+    localError(c.config, info, "system needs: nimCreateFlowVar")
   var bindings: TIdTable
   initIdTable(bindings)
   bindings.idTablePut(sym.ast[genericParamsPos].sons[0].typ, t)
@@ -1852,10 +1869,10 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
   result = n
   case s.magic # magics that need special treatment
   of mAddr:
-    checkSonsLen(n, 2)
+    checkSonsLen(n, 2, c.config)
     result = semAddr(c, n.sons[1], s.name.s == "unsafeAddr")
   of mTypeOf:
-    checkSonsLen(n, 2)
+    checkSonsLen(n, 2, c.config)
     result = semTypeOf(c, n.sons[1])
   #of mArrGet: result = semArrGet(c, n, flags)
   #of mArrPut: result = semArrPut(c, n, flags)
@@ -1872,12 +1889,12 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
   of mExpandToAst: result = semExpandToAst(c, n, s, flags)
   of mQuoteAst: result = semQuoteAst(c, n)
   of mAstToStr:
-    checkSonsLen(n, 2)
-    result = newStrNodeT(renderTree(n[1], {renderNoComments}), n)
-    result.typ = getSysType(tyString)
+    checkSonsLen(n, 2, c.config)
+    result = newStrNodeT(renderTree(n[1], {renderNoComments}), n, c.graph)
+    result.typ = getSysType(c.graph, n.info, tyString)
   of mParallel:
-    if not experimentalMode(c):
-      localError(n.info, "use the {.experimental.} pragma to enable 'parallel'")
+    if parallel notin c.features:
+      localError(c.config, n.info, "use the {.experimental.} pragma to enable 'parallel'")
     result = setMs(n, s)
     var x = n.lastSon
     if x.kind == nkDo: x = x.sons[bodyPos]
@@ -1918,7 +1935,7 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
       if callee.magic != mNone:
         result = magicsAfterOverloadResolution(c, result, flags)
   of mRunnableExamples:
-    if gCmd == cmdDoc and n.len >= 2 and n.lastSon.kind == nkStmtList:
+    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)
         if c.runnableExamples == nil:
@@ -1963,7 +1980,7 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode =
     var it = n.sons[i]
     case it.kind
     of nkElifBranch, nkElifExpr:
-      checkSonsLen(it, 2)
+      checkSonsLen(it, 2, c.config)
       if whenNimvm:
         if semCheck:
           it.sons[1] = semExpr(c, it.sons[1])
@@ -1978,14 +1995,14 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode =
         elif e.intVal != 0 and result == nil:
           setResult(it.sons[1])
     of nkElse, nkElseExpr:
-      checkSonsLen(it, 1)
+      checkSonsLen(it, 1, c.config)
       if result == nil or whenNimvm:
         if semCheck:
           it.sons[0] = semExpr(c, it.sons[0])
           typ = commonType(typ, it.sons[0].typ)
         if result == nil:
           result = it.sons[0]
-    else: illFormedAst(n)
+    else: illFormedAst(n, c.config)
   if result == nil:
     result = newNodeI(nkEmpty, n.info)
   if whenNimvm: result.typ = typ
@@ -2004,7 +2021,7 @@ proc semSetConstr(c: PContext, n: PNode): PNode =
     var typ: PType = nil
     for i in countup(0, sonsLen(n) - 1):
       if isRange(n.sons[i]):
-        checkSonsLen(n.sons[i], 3)
+        checkSonsLen(n.sons[i], 3, c.config)
         n.sons[i].sons[1] = semExprWithType(c, n.sons[i].sons[1])
         n.sons[i].sons[2] = semExprWithType(c, n.sons[i].sons[2])
         if typ == nil:
@@ -2021,7 +2038,7 @@ proc semSetConstr(c: PContext, n: PNode): PNode =
         if typ == nil:
           typ = skipTypes(n.sons[i].typ, {tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink})
     if not isOrdinalType(typ):
-      localError(n.info, errOrdinalTypeExpected)
+      localError(c.config, n.info, errOrdinalTypeExpected)
       typ = makeRangeType(c, 0, MaxSetElements-1, n.info)
     elif lengthOrd(typ) > MaxSetElements:
       typ = makeRangeType(c, 0, MaxSetElements-1, n.info)
@@ -2059,14 +2076,14 @@ proc semTableConstr(c: PContext, n: PNode): PNode =
 
       lastKey = i+1
 
-  if lastKey != n.len: illFormedAst(n)
+  if lastKey != n.len: illFormedAst(n, c.config)
   result = semExpr(c, result)
 
 type
   TParKind = enum
     paNone, paSingle, paTupleFields, paTuplePositions
 
-proc checkPar(n: PNode): TParKind =
+proc checkPar(c: PContext; n: PNode): TParKind =
   var length = sonsLen(n)
   if length == 0:
     result = paTuplePositions # ()
@@ -2081,11 +2098,11 @@ proc checkPar(n: PNode): TParKind =
       if result == paTupleFields:
         if (n.sons[i].kind != nkExprColonExpr) or
             not (n.sons[i].sons[0].kind in {nkSym, nkIdent}):
-          localError(n.sons[i].info, errNamedExprExpected)
+          localError(c.config, n.sons[i].info, errNamedExprExpected)
           return paNone
       else:
         if n.sons[i].kind == nkExprColonExpr:
-          localError(n.sons[i].info, errNamedExprNotAllowed)
+          localError(c.config, n.sons[i].info, errNamedExprNotAllowed)
           return paNone
 
 proc semTupleFieldsConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
@@ -2095,12 +2112,12 @@ proc semTupleFieldsConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
   var ids = initIntSet()
   for i in countup(0, sonsLen(n) - 1):
     if n[i].kind != nkExprColonExpr or n[i][0].kind notin {nkSym, nkIdent}:
-      illFormedAst(n.sons[i])
+      illFormedAst(n.sons[i], c.config)
     var id: PIdent
     if n.sons[i].sons[0].kind == nkIdent: id = n.sons[i].sons[0].ident
     else: id = n.sons[i].sons[0].sym.name
     if containsOrIncl(ids, id.id):
-      localError(n.sons[i].info, errFieldInitTwice, id.s)
+      localError(c.config, n.sons[i].info, errFieldInitTwice % id.s)
     n.sons[i].sons[1] = semExprWithType(c, n.sons[i].sons[1],
                                         flags*{efAllowDestructor})
     var f = newSymS(skField, n.sons[i].sons[0], c)
@@ -2134,14 +2151,14 @@ include semobjconstr
 proc semBlock(c: PContext, n: PNode): PNode =
   result = n
   inc(c.p.nestedBlockCounter)
-  checkSonsLen(n, 2)
+  checkSonsLen(n, 2, c.config)
   openScope(c) # BUGFIX: label is in the scope of block!
   if n.sons[0].kind != nkEmpty:
     var labl = newSymG(skLabel, n.sons[0], c)
     if sfGenSym notin labl.flags:
       addDecl(c, labl)
     n.sons[0] = newSymNode(labl, n.sons[0].info)
-    suggestSym(n.sons[0].info, labl, c.graph.usageSym)
+    suggestSym(c.config, n.sons[0].info, labl, c.graph.usageSym)
     styleCheckDef(labl)
   n.sons[1] = semExpr(c, n.sons[1])
   n.typ = n.sons[1].typ
@@ -2150,15 +2167,31 @@ proc semBlock(c: PContext, n: PNode): PNode =
   closeScope(c)
   dec(c.p.nestedBlockCounter)
 
+proc semExportExcept(c: PContext, n: PNode): PNode =
+  let moduleName = semExpr(c, n[0])
+  if moduleName.kind != nkSym or moduleName.sym.kind != skModule:
+    localError(c.config, n.info, "The export/except syntax expects a module name")
+    return n
+  let exceptSet = readExceptSet(c, n)
+  let exported = moduleName.sym
+  strTableAdd(c.module.tab, exported)
+  var i: TTabIter
+  var s = initTabIter(i, exported.tab)
+  while s != nil:
+    if s.kind in ExportableSymKinds+{skModule} and
+       s.name.id notin exceptSet:
+      strTableAdd(c.module.tab, s)
+    s = nextIter(i, exported.tab)
+  result = n
+
 proc semExport(c: PContext, n: PNode): PNode =
   var x = newNodeI(n.kind, n.info)
-  #let L = if n.kind == nkExportExceptStmt: L = 1 else: n.len
   for i in 0..<n.len:
     let a = n.sons[i]
     var o: TOverloadIter
     var s = initOverloadIter(o, c, a)
     if s == nil:
-      localError(a.info, errGenerated, "cannot export: " & renderTree(a))
+      localError(c.config, a.info, errGenerated, "cannot export: " & renderTree(a))
     elif s.kind == skModule:
       # forward everything from that module:
       strTableAdd(c.module.tab, s)
@@ -2192,7 +2225,7 @@ proc shouldBeBracketExpr(n: PNode): bool =
 
 proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
   result = n
-  if gCmd == cmdIdeTools: suggestExpr(c, n)
+  if c.config.cmd == cmdIdeTools: suggestExpr(c, n)
   if nfSem in n.flags: return
   case n.kind
   of nkIdent, nkAccQuoted:
@@ -2211,7 +2244,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
       if result.kind == nkSym:
         markIndirect(c, result.sym)
         # if isGenericRoutine(result.sym):
-        #   localError(n.info, errInstantiateXExplicitly, s.name.s)
+        #   localError(c.config, n.info, errInstantiateXExplicitly, s.name.s)
   of nkSym:
     # because of the changed symbol binding, this does not mean that we
     # don't have to check the symbol for semantics here again!
@@ -2219,46 +2252,46 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
   of nkEmpty, nkNone, nkCommentStmt, nkType:
     discard
   of nkNilLit:
-    if result.typ == nil: result.typ = getSysType(tyNil)
+    if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyNil)
   of nkIntLit:
-    if result.typ == nil: setIntLitType(result)
+    if result.typ == nil: setIntLitType(c.graph, result)
   of nkInt8Lit:
-    if result.typ == nil: result.typ = getSysType(tyInt8)
+    if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyInt8)
   of nkInt16Lit:
-    if result.typ == nil: result.typ = getSysType(tyInt16)
+    if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyInt16)
   of nkInt32Lit:
-    if result.typ == nil: result.typ = getSysType(tyInt32)
+    if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyInt32)
   of nkInt64Lit:
-    if result.typ == nil: result.typ = getSysType(tyInt64)
+    if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyInt64)
   of nkUIntLit:
-    if result.typ == nil: result.typ = getSysType(tyUInt)
+    if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyUInt)
   of nkUInt8Lit:
-    if result.typ == nil: result.typ = getSysType(tyUInt8)
+    if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyUInt8)
   of nkUInt16Lit:
-    if result.typ == nil: result.typ = getSysType(tyUInt16)
+    if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyUInt16)
   of nkUInt32Lit:
-    if result.typ == nil: result.typ = getSysType(tyUInt32)
+    if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyUInt32)
   of nkUInt64Lit:
-    if result.typ == nil: result.typ = getSysType(tyUInt64)
+    if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyUInt64)
   #of nkFloatLit:
   #  if result.typ == nil: result.typ = getFloatLitType(result)
   of nkFloat32Lit:
-    if result.typ == nil: result.typ = getSysType(tyFloat32)
+    if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyFloat32)
   of nkFloat64Lit, nkFloatLit:
-    if result.typ == nil: result.typ = getSysType(tyFloat64)
+    if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyFloat64)
   of nkFloat128Lit:
-    if result.typ == nil: result.typ = getSysType(tyFloat128)
+    if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyFloat128)
   of nkStrLit..nkTripleStrLit:
-    if result.typ == nil: result.typ = getSysType(tyString)
+    if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyString)
   of nkCharLit:
-    if result.typ == nil: result.typ = getSysType(tyChar)
+    if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyChar)
   of nkDotExpr:
     result = semFieldAccess(c, n, flags)
     if result.kind == nkDotCall:
       result.kind = nkCall
       result = semExpr(c, result, flags)
   of nkBind:
-    message(n.info, warnDeprecated, "bind")
+    message(c.config, n.info, warnDeprecated, "bind")
     result = semExpr(c, n.sons[0], flags)
   of nkTypeOfExpr, nkTupleTy, nkTupleClassTy, nkRefTy..nkEnumTy, nkStaticTy:
     if c.matchedConcept != nil and n.len == 1:
@@ -2271,13 +2304,13 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
     result.typ = makeTypeDesc(c, typ)
   of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit:
     # check if it is an expression macro:
-    checkMinSonsLen(n, 1)
+    checkMinSonsLen(n, 1, c.config)
     #when defined(nimsuggest):
     #  if gIdeCmd == ideCon and gTrackPos == 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:
-      #if gCmd == cmdPretty and n.sons[0].kind == nkDotExpr:
+      #if c.config.cmd == cmdPretty and n.sons[0].kind == nkDotExpr:
       #  pretty.checkUse(n.sons[0].sons[1].info, s)
       case s.kind
       of skMacro:
@@ -2327,7 +2360,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
       else:
         result = semExpr(c, result, flags)
   of nkBracketExpr:
-    checkMinSonsLen(n, 1)
+    checkMinSonsLen(n, 1, c.config)
     result = semArrayAccess(c, n, flags)
   of nkCurlyExpr:
     result = semExpr(c, buildOverloadedSubscripts(n, getIdent"{}"), flags)
@@ -2335,7 +2368,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
     var
       expr = n[0]
       pragma = n[1]
-      pragmaName = considerQuotedIdent(pragma[0])
+      pragmaName = considerQuotedIdent(c.config, pragma[0])
       flags = flags
 
     case whichKeyword(pragmaName)
@@ -2343,11 +2376,11 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
       flags.incl efExplain
     else:
       # what other pragmas are allowed for expressions? `likely`, `unlikely`
-      invalidPragma(n)
+      invalidPragma(c, n)
 
     result = semExpr(c, n[0], flags)
   of nkPar, nkTupleConstr:
-    case checkPar(n)
+    case checkPar(c, n)
     of paNone: result = errorNode(c, n)
     of paTuplePositions:
       var tupexp = semTuplePositionsConstr(c, n, flags)
@@ -2366,24 +2399,24 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
   of nkDerefExpr: result = semDeref(c, n)
   of nkAddr:
     result = n
-    checkSonsLen(n, 1)
+    checkSonsLen(n, 1, c.config)
     result = semAddr(c, n.sons[0])
   of nkHiddenAddr, nkHiddenDeref:
-    checkSonsLen(n, 1)
+    checkSonsLen(n, 1, c.config)
     n.sons[0] = semExpr(c, n.sons[0], flags)
   of nkCast: result = semCast(c, n)
   of nkIfExpr, nkIfStmt: result = semIf(c, n)
   of nkHiddenStdConv, nkHiddenSubConv, nkConv, nkHiddenCallConv:
-    checkSonsLen(n, 2)
+    checkSonsLen(n, 2, c.config)
     considerGenSyms(c, n)
   of nkStringToCString, nkCStringToString, nkObjDownConv, nkObjUpConv:
-    checkSonsLen(n, 1)
+    checkSonsLen(n, 1, c.config)
     considerGenSyms(c, n)
   of nkChckRangeF, nkChckRange64, nkChckRange:
-    checkSonsLen(n, 3)
+    checkSonsLen(n, 3, c.config)
     considerGenSyms(c, n)
   of nkCheckedFieldExpr:
-    checkMinSonsLen(n, 2)
+    checkMinSonsLen(n, 2, c.config)
     considerGenSyms(c, n)
   of nkTableConstr:
     result = semTableConstr(c, n)
@@ -2427,20 +2460,23 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
     #
     # works:
     if c.currentScope.depthLevel > 2 + c.compilesContextId:
-      localError(n.info, errXOnlyAtModuleScope, "import")
+      localError(c.config, n.info, errXOnlyAtModuleScope % "import")
     result = evalImport(c, n)
   of nkImportExceptStmt:
-    if not isTopLevel(c): localError(n.info, errXOnlyAtModuleScope, "import")
+    if not isTopLevel(c): localError(c.config, n.info, errXOnlyAtModuleScope % "import")
     result = evalImportExcept(c, n)
   of nkFromStmt:
-    if not isTopLevel(c): localError(n.info, errXOnlyAtModuleScope, "from")
+    if not isTopLevel(c): localError(c.config, n.info, errXOnlyAtModuleScope % "from")
     result = evalFrom(c, n)
   of nkIncludeStmt:
-    #if not isTopLevel(c): localError(n.info, errXOnlyAtModuleScope, "include")
+    #if not isTopLevel(c): localError(c.config, n.info, errXOnlyAtModuleScope % "include")
     result = evalInclude(c, n)
-  of nkExportStmt, nkExportExceptStmt:
-    if not isTopLevel(c): localError(n.info, errXOnlyAtModuleScope, "export")
+  of nkExportStmt:
+    if not isTopLevel(c): localError(c.config, n.info, errXOnlyAtModuleScope % "export")
     result = semExport(c, n)
+  of nkExportExceptStmt:
+    if not isTopLevel(c): localError(c.config, n.info, errXOnlyAtModuleScope % "export")
+    result = semExportExcept(c, n)
   of nkPragmaBlock:
     result = semPragmaBlock(c, n)
   of nkStaticStmt:
@@ -2448,14 +2484,14 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
   of nkDefer:
     n.sons[0] = semExpr(c, n.sons[0])
     if not n.sons[0].typ.isEmptyType and not implicitlyDiscardable(n.sons[0]):
-      localError(n.info, errGenerated, "'defer' takes a 'void' expression")
-    #localError(n.info, errGenerated, "'defer' not allowed in this context")
+      localError(c.config, n.info, "'defer' takes a 'void' expression")
+    #localError(c.config, n.info, errGenerated, "'defer' not allowed in this context")
   of nkGotoState, nkState:
-    if n.len != 1 and n.len != 2: illFormedAst(n)
+    if n.len != 1 and n.len != 2: illFormedAst(n, c.config)
     for i in 0 ..< n.len:
       n.sons[i] = semExpr(c, n.sons[i])
   of nkComesFrom: discard "ignore the comes from information for now"
   else:
-    localError(n.info, errInvalidExpressionX,
+    localError(c.config, n.info, "invalid expression: " &
                renderTree(n, {renderNoComments}))
   if result != nil: incl(result.flags, nfSem)
diff --git a/compiler/semfields.nim b/compiler/semfields.nim
index c5bc07d77..16d79c559 100644
--- a/compiler/semfields.nim
+++ b/compiler/semfields.nim
@@ -16,16 +16,17 @@ type
     tupleIndex: int
     field: PSym
     replaceByFieldName: bool
+    c: PContext
 
 proc instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode =
   case n.kind
   of nkEmpty..pred(nkIdent), succ(nkSym)..nkNilLit: result = n
   of nkIdent, nkSym:
     result = n
-    let ident = considerQuotedIdent(n)
+    let ident = considerQuotedIdent(c.c.config, n)
     var L = sonsLen(forLoop)
     if c.replaceByFieldName:
-      if ident.id == considerQuotedIdent(forLoop[0]).id:
+      if ident.id == considerQuotedIdent(c.c.config, 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
@@ -33,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(forLoop[i]).id:
+      if ident.id == considerQuotedIdent(c.c.config, forLoop[i]).id:
         var call = forLoop.sons[L-2]
         var tupl = call.sons[i+1-ord(c.replaceByFieldName)]
         if c.field.isNil:
@@ -47,7 +48,7 @@ proc instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode =
         break
   else:
     if n.kind == nkContinueStmt:
-      localError(n.info, errGenerated,
+      localError(c.c.config, n.info,
                  "'continue' not supported in a 'fields' loop")
     result = copyNode(n)
     newSons(result, sonsLen(n))
@@ -63,6 +64,7 @@ proc semForObjectFields(c: TFieldsCtx, typ, forLoop, father: PNode) =
   case typ.kind
   of nkSym:
     var fc: TFieldInstCtx  # either 'tup[i]' or 'field' is valid
+    fc.c = c.c
     fc.field = typ.sym
     fc.replaceByFieldName = c.m == mFieldPairs
     openScope(c.c)
@@ -76,7 +78,7 @@ proc semForObjectFields(c: TFieldsCtx, typ, forLoop, father: PNode) =
     let L = forLoop.len
     let call = forLoop.sons[L-2]
     if call.len > 2:
-      localError(forLoop.info, errGenerated,
+      localError(c.c.config, forLoop.info,
                  "parallel 'fields' iterator does not work for 'case' objects")
       return
     # iterate over the selector:
@@ -99,17 +101,17 @@ proc semForObjectFields(c: TFieldsCtx, typ, forLoop, father: PNode) =
   of nkRecList:
     for t in items(typ): semForObjectFields(c, t, forLoop, father)
   else:
-    illFormedAstLocal(typ)
+    illFormedAstLocal(typ, c.c.config)
 
 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(magicsys.systemModule.tab, getIdent"true")
+  var trueSymbol = strTableGet(c.graph.systemModule.tab, getIdent"true")
   if trueSymbol == nil:
-    localError(n.info, errSystemNeeds, "true")
+    localError(c.config, n.info, "system needs: 'true'")
     trueSymbol = newSym(skUnknown, getIdent"true", getCurrOwner(c), n.info)
-    trueSymbol.typ = getSysType(tyBool)
+    trueSymbol.typ = getSysType(c.graph, n.info, tyBool)
 
   result.sons[0] = newSymNode(trueSymbol, n.info)
   var stmts = newNodeI(nkStmtList, n.info)
@@ -118,18 +120,18 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode =
   var length = sonsLen(n)
   var call = n.sons[length-2]
   if length-2 != sonsLen(call)-1 + ord(m==mFieldPairs):
-    localError(n.info, errWrongNumberOfVariables)
+    localError(c.config, n.info, errWrongNumberOfVariables)
     return result
 
   const skippedTypesForFields = abstractVar - {tyTypeDesc} + tyUserTypeClasses
   var tupleTypeA = skipTypes(call.sons[1].typ, skippedTypesForFields)
   if tupleTypeA.kind notin {tyTuple, tyObject}:
-    localError(n.info, errGenerated, "no object or tuple type")
+    localError(c.config, n.info, errGenerated, "no object or tuple type")
     return result
   for i in 1..call.len-1:
     var tupleTypeB = skipTypes(call.sons[i].typ, skippedTypesForFields)
     if not sameType(tupleTypeA, tupleTypeB):
-      typeMismatch(call.sons[i].info, tupleTypeA, tupleTypeB)
+      typeMismatch(c.config, call.sons[i].info, tupleTypeA, tupleTypeB)
 
   inc(c.p.nestedLoopCounter)
   if tupleTypeA.kind == tyTuple:
@@ -139,6 +141,7 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode =
       var fc: TFieldInstCtx
       fc.tupleType = tupleTypeA
       fc.tupleIndex = i
+      fc.c = c
       fc.replaceByFieldName = m == mFieldPairs
       var body = instFieldLoopBody(fc, loopBody, n)
       inc c.inUnrolledContext
diff --git a/compiler/semfold.nim b/compiler/semfold.nim
index 096fc19e0..daf9ce983 100644
--- a/compiler/semfold.nim
+++ b/compiler/semfold.nim
@@ -13,27 +13,16 @@
 import
   strutils, options, ast, astalgo, trees, treetab, nimsets, times,
   nversion, platform, math, msgs, os, condsyms, idents, renderer, types,
-  commands, magicsys, saturate
+  commands, magicsys, modulegraphs, strtabs
 
-proc getConstExpr*(m: PSym, n: PNode): PNode
-  # evaluates the constant expression or returns nil if it is no constant
-  # expression
-proc evalOp*(m: TMagic, n, a, b, c: PNode): PNode
-proc leValueConv*(a, b: PNode): bool
-proc newIntNodeT*(intVal: BiggestInt, n: PNode): PNode
-proc newFloatNodeT(floatVal: BiggestFloat, n: PNode): PNode
-proc newStrNodeT*(strVal: string, n: PNode): PNode
-
-# implementation
-
-proc newIntNodeT(intVal: BiggestInt, n: PNode): PNode =
+proc newIntNodeT*(intVal: BiggestInt, n: PNode; g: ModuleGraph): PNode =
   case skipTypes(n.typ, abstractVarRange).kind
   of tyInt:
     result = newIntNode(nkIntLit, intVal)
     # See bug #6989. 'pred' et al only produce an int literal type if the
     # original type was 'int', not a distinct int etc.
     if n.typ.kind == tyInt:
-      result.typ = getIntLitType(result)
+      result.typ = getIntLitType(g, result)
     else:
       result.typ = n.typ
     # hrm, this is not correct: 1 + high(int) shouldn't produce tyInt64 ...
@@ -46,20 +35,82 @@ proc newIntNodeT(intVal: BiggestInt, n: PNode): PNode =
     result.typ = n.typ
   result.info = n.info
 
-proc newFloatNodeT(floatVal: BiggestFloat, n: PNode): PNode =
+proc newFloatNodeT*(floatVal: BiggestFloat, n: PNode; g: ModuleGraph): PNode =
   result = newFloatNode(nkFloatLit, floatVal)
   if skipTypes(n.typ, abstractVarRange).kind == tyFloat:
-    result.typ = getFloatLitType(result)
+    result.typ = getFloatLitType(g, result)
   else:
     result.typ = n.typ
   result.info = n.info
 
-proc newStrNodeT(strVal: string, n: PNode): PNode =
+proc newStrNodeT*(strVal: string, n: PNode; g: ModuleGraph): PNode =
   result = newStrNode(nkStrLit, strVal)
   result.typ = n.typ
   result.info = n.info
 
-proc ordinalValToString*(a: PNode): string =
+proc getConstExpr*(m: PSym, n: PNode; g: ModuleGraph): PNode
+  # evaluates the constant expression or returns nil if it is no constant
+  # 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):
+    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):
+    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):
+    result = newIntNodeT(res, n, g)
+
+proc foldAbs*(a: BiggestInt, n: PNode; g: ModuleGraph): PNode =
+  if a != firstOrd(n.typ):
+    result = newIntNodeT(a, n, g)
+
+proc foldMod*(a, b: BiggestInt, n: PNode; g: ModuleGraph): PNode =
+  if b != 0'i64:
+    result = newIntNodeT(a mod b, n, g)
+
+proc foldModU*(a, b: BiggestInt, n: PNode; g: ModuleGraph): PNode =
+  if b != 0'i64:
+    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):
+    result = newIntNodeT(a div b, n, g)
+
+proc foldDivU*(a, b: BiggestInt, n: PNode; g: ModuleGraph): PNode =
+  if b != 0'i64:
+    result = newIntNodeT(a /% b, n, g)
+
+proc foldMul*(a, b: BiggestInt, n: PNode; g: ModuleGraph): PNode =
+  let res = a *% b
+  let floatProd = toBiggestFloat(a) * toBiggestFloat(b)
+  let resAsFloat = toBiggestFloat(res)
+
+  # Fast path for normal case: small multiplicands, and no info
+  # is lost in either method.
+  if resAsFloat == floatProd and checkInRange(n, res):
+    return newIntNodeT(res, n, g)
+
+  # Somebody somewhere lost info. Close enough, or way off? Note
+  # that a != 0 and b != 0 (else resAsFloat == floatProd == 0).
+  # The difference either is or isn't significant compared to the
+  # true value (of which floatProd is a good approximation).
+
+  # 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):
+    return newIntNodeT(res, n, g)
+
+proc ordinalValToString*(a: PNode; g: ModuleGraph): string =
   # because $ has the param ordinal[T], `a` is not necessarily an enum, but an
   # ordinal
   var x = getInt(a)
@@ -71,14 +122,14 @@ proc ordinalValToString*(a: PNode): string =
   of tyEnum:
     var n = t.n
     for i in countup(0, sonsLen(n) - 1):
-      if n.sons[i].kind != nkSym: internalError(a.info, "ordinalValToString")
+      if n.sons[i].kind != nkSym: internalError(g.config, a.info, "ordinalValToString")
       var field = n.sons[i].sym
       if field.position == x:
         if field.ast == nil:
           return field.name.s
         else:
           return field.ast.strVal
-    internalError(a.info, "no symbol for ordinal value: " & $x)
+    internalError(g.config, a.info, "no symbol for ordinal value: " & $x)
   else:
     result = $x
 
@@ -97,12 +148,12 @@ proc pickIntRange(a, b: PType): PType =
 proc isIntRangeOrLit(t: PType): bool =
   result = isIntRange(t) or isIntLit(t)
 
-proc makeRange(typ: PType, first, last: BiggestInt): PType =
+proc makeRange(typ: PType, first, last: BiggestInt; g: ModuleGraph): PType =
   let minA = min(first, last)
   let maxA = max(first, last)
   let lowerNode = newIntNode(nkIntLit, minA)
   if typ.kind == tyInt and minA == maxA:
-    result = getIntLitType(lowerNode)
+    result = getIntLitType(g, lowerNode)
   elif typ.kind in {tyUint, tyUInt64}:
     # these are not ordinal types, so you get no subrange type for these:
     result = typ
@@ -114,7 +165,7 @@ proc makeRange(typ: PType, first, last: BiggestInt): PType =
     result.n = n
     addSonSkipIntLit(result, skipTypes(typ, {tyRange}))
 
-proc makeRangeF(typ: PType, first, last: BiggestFloat): PType =
+proc makeRangeF(typ: PType, first, last: BiggestFloat; g: ModuleGraph): PType =
   var n = newNode(nkRange)
   addSon(n, newFloatNode(nkFloatLit, min(first.float, last.float)))
   addSon(n, newFloatNode(nkFloatLit, max(first.float, last.float)))
@@ -124,9 +175,9 @@ proc makeRangeF(typ: PType, first, last: BiggestFloat): PType =
 
 proc evalIs(n, a: PNode): PNode =
   # XXX: This should use the standard isOpImpl
-  internalAssert a.kind == nkSym and a.sym.kind == skType
-  internalAssert n.sonsLen == 3 and
-    n[2].kind in {nkStrLit..nkTripleStrLit, nkType}
+  #internalAssert a.kind == nkSym and a.sym.kind == skType
+  #internalAssert n.sonsLen == 3 and
+  #  n[2].kind in {nkStrLit..nkTripleStrLit, nkType}
 
   let t1 = a.sym.typ
 
@@ -150,126 +201,113 @@ proc evalIs(n, a: PNode): PNode =
     result = newIntNode(nkIntLit, ord(match))
   result.typ = n.typ
 
-proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
+proc evalOp(m: TMagic, n, a, b, c: PNode; g: ModuleGraph): PNode =
   # b and c may be nil
   result = nil
   case m
-  of mOrd: result = newIntNodeT(getOrdValue(a), n)
-  of mChr: result = newIntNodeT(getInt(a), n)
-  of mUnaryMinusI, mUnaryMinusI64: result = newIntNodeT(- getInt(a), n)
-  of mUnaryMinusF64: result = newFloatNodeT(- getFloat(a), n)
-  of mNot: result = newIntNodeT(1 - getInt(a), n)
-  of mCard: result = newIntNodeT(nimsets.cardSet(a), n)
-  of mBitnotI: result = newIntNodeT(not getInt(a), n)
-  of mLengthArray: result = newIntNodeT(lengthOrd(a.typ), n)
+  of mOrd: result = newIntNodeT(getOrdValue(a), n, g)
+  of mChr: result = newIntNodeT(getInt(a), n, g)
+  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 mLengthSeq, mLengthOpenArray, mXLenSeq, mLengthStr, mXLenStr:
     if a.kind == nkNilLit:
-      result = newIntNodeT(0, n)
+      result = newIntNodeT(0, n, g)
     elif a.kind in {nkStrLit..nkTripleStrLit}:
-      result = newIntNodeT(len a.strVal, n)
+      result = newIntNodeT(len a.strVal, n, g)
     else:
-      result = newIntNodeT(sonsLen(a), n) # BUGFIX
+      result = newIntNodeT(sonsLen(a), n, g)
   of mUnaryPlusI, mUnaryPlusF64: result = a # throw `+` away
   of mToFloat, mToBiggestFloat:
-    result = newFloatNodeT(toFloat(int(getInt(a))), n)
-  of mToInt, mToBiggestInt: result = newIntNodeT(system.toInt(getFloat(a)), n)
-  of mAbsF64: result = newFloatNodeT(abs(getFloat(a)), n)
-  of mAbsI:
-    if getInt(a) >= 0: result = a
-    else: result = newIntNodeT(- getInt(a), n)
+    result = newFloatNodeT(toFloat(int(getInt(a))), n, g)
+  # XXX: Hides overflow/underflow
+  of mToInt, mToBiggestInt: result = newIntNodeT(system.toInt(getFloat(a)), n, g)
+  of mAbsF64: result = newFloatNodeT(abs(getFloat(a)), n, g)
+  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)
-  of mToU8: result = newIntNodeT(getInt(a) and 0x000000FF, n)
-  of mToU16: result = newIntNodeT(getInt(a) and 0x0000FFFF, n)
-  of mToU32: result = newIntNodeT(getInt(a) and 0x00000000FFFFFFFF'i64, n)
-  of mUnaryLt: result = newIntNodeT(getOrdValue(a) |-| 1, n)
-  of mSucc: result = newIntNodeT(getOrdValue(a) |+| getInt(b), n)
-  of mPred: result = newIntNodeT(getOrdValue(a) |-| getInt(b), n)
-  of mAddI: result = newIntNodeT(getInt(a) |+| getInt(b), n)
-  of mSubI: result = newIntNodeT(getInt(a) |-| getInt(b), n)
-  of mMulI: result = newIntNodeT(getInt(a) |*| getInt(b), n)
+    result = newIntNodeT(getInt(a) and (`shl`(1, getSize(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)
+  of mUnaryLt: result = foldSub(getOrdValue(a), 1, n, g)
+  of mSucc: result = foldAdd(getOrdValue(a), getInt(b), n, g)
+  of mPred: result = foldSub(getOrdValue(a), getInt(b), n, g)
+  of mAddI: result = foldAdd(getInt(a), getInt(b), n, g)
+  of mSubI: result = foldSub(getInt(a), getInt(b), n, g)
+  of mMulI: result = foldMul(getInt(a), getInt(b), n, g)
   of mMinI:
-    if getInt(a) > getInt(b): result = newIntNodeT(getInt(b), n)
-    else: result = newIntNodeT(getInt(a), n)
+    if getInt(a) > getInt(b): result = newIntNodeT(getInt(b), n, g)
+    else: result = newIntNodeT(getInt(a), n, g)
   of mMaxI:
-    if getInt(a) > getInt(b): result = newIntNodeT(getInt(a), n)
-    else: result = newIntNodeT(getInt(b), n)
+    if getInt(a) > getInt(b): result = newIntNodeT(getInt(a), n, g)
+    else: result = newIntNodeT(getInt(b), n, g)
   of mShlI:
     case skipTypes(n.typ, abstractRange).kind
-    of tyInt8: result = newIntNodeT(int8(getInt(a)) shl int8(getInt(b)), n)
-    of tyInt16: result = newIntNodeT(int16(getInt(a)) shl int16(getInt(b)), n)
-    of tyInt32: result = newIntNodeT(int32(getInt(a)) shl int32(getInt(b)), n)
+    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:
-      result = newIntNodeT(`shl`(getInt(a), getInt(b)), n)
-    else: internalError(n.info, "constant folding for shl")
+      result = newIntNodeT(`shl`(getInt(a), getInt(b)), n, g)
+    else: internalError(g.config, n.info, "constant folding for shl")
   of mShrI:
     case skipTypes(n.typ, abstractRange).kind
-    of tyInt8: result = newIntNodeT(int8(getInt(a)) shr int8(getInt(b)), n)
-    of tyInt16: result = newIntNodeT(int16(getInt(a)) shr int16(getInt(b)), n)
-    of tyInt32: result = newIntNodeT(int32(getInt(a)) shr int32(getInt(b)), n)
+    of tyInt8: result = newIntNodeT(int8(getInt(a)) shr int8(getInt(b)), n, g)
+    of tyInt16: result = newIntNodeT(int16(getInt(a)) shr int16(getInt(b)), n, g)
+    of tyInt32: result = newIntNodeT(int32(getInt(a)) shr int32(getInt(b)), n, g)
     of tyInt64, tyInt, tyUInt..tyUInt64:
-      result = newIntNodeT(`shr`(getInt(a), getInt(b)), n)
-    else: internalError(n.info, "constant folding for shr")
-  of mDivI:
-    let y = getInt(b)
-    if y != 0:
-      result = newIntNodeT(`|div|`(getInt(a), y), n)
-  of mModI:
-    let y = getInt(b)
-    if y != 0:
-      result = newIntNodeT(`|mod|`(getInt(a), y), n)
-  of mAddF64: result = newFloatNodeT(getFloat(a) + getFloat(b), n)
-  of mSubF64: result = newFloatNodeT(getFloat(a) - getFloat(b), n)
-  of mMulF64: result = newFloatNodeT(getFloat(a) * getFloat(b), n)
+      result = newIntNodeT(`shr`(getInt(a), getInt(b)), n, g)
+    else: internalError(g.config, n.info, "constant folding for shr")
+  of mDivI: result = foldDiv(getInt(a), getInt(b), n, g)
+  of mModI: result = foldMod(getInt(a), getInt(b), n, g)
+  of mAddF64: result = newFloatNodeT(getFloat(a) + getFloat(b), n, g)
+  of mSubF64: result = newFloatNodeT(getFloat(a) - getFloat(b), n, g)
+  of mMulF64: result = newFloatNodeT(getFloat(a) * getFloat(b), n, g)
   of mDivF64:
     if getFloat(b) == 0.0:
-      if getFloat(a) == 0.0: result = newFloatNodeT(NaN, n)
-      elif getFloat(b).classify == fcNegZero: result = newFloatNodeT(-Inf, n)
-      else: result = newFloatNodeT(Inf, n)
+      if getFloat(a) == 0.0: result = newFloatNodeT(NaN, n, g)
+      elif getFloat(b).classify == fcNegZero: result = newFloatNodeT(-Inf, n, g)
+      else: result = newFloatNodeT(Inf, n, g)
     else:
-      result = newFloatNodeT(getFloat(a) / getFloat(b), n)
+      result = newFloatNodeT(getFloat(a) / getFloat(b), n, g)
   of mMaxF64:
-    if getFloat(a) > getFloat(b): result = newFloatNodeT(getFloat(a), n)
-    else: result = newFloatNodeT(getFloat(b), n)
+    if getFloat(a) > getFloat(b): result = newFloatNodeT(getFloat(a), n, g)
+    else: result = newFloatNodeT(getFloat(b), n, g)
   of mMinF64:
-    if getFloat(a) > getFloat(b): result = newFloatNodeT(getFloat(b), n)
-    else: result = newFloatNodeT(getFloat(a), n)
-  of mIsNil: result = newIntNodeT(ord(a.kind == nkNilLit), n)
+    if getFloat(a) > getFloat(b): result = newFloatNodeT(getFloat(b), n, g)
+    else: result = newFloatNodeT(getFloat(a), n, g)
+  of mIsNil: result = newIntNodeT(ord(a.kind == nkNilLit), n, g)
   of mLtI, mLtB, mLtEnum, mLtCh:
-    result = newIntNodeT(ord(getOrdValue(a) < getOrdValue(b)), n)
+    result = newIntNodeT(ord(getOrdValue(a) < getOrdValue(b)), n, g)
   of mLeI, mLeB, mLeEnum, mLeCh:
-    result = newIntNodeT(ord(getOrdValue(a) <= getOrdValue(b)), n)
+    result = newIntNodeT(ord(getOrdValue(a) <= getOrdValue(b)), n, g)
   of mEqI, mEqB, mEqEnum, mEqCh:
-    result = newIntNodeT(ord(getOrdValue(a) == getOrdValue(b)), n)
-  of mLtF64: result = newIntNodeT(ord(getFloat(a) < getFloat(b)), n)
-  of mLeF64: result = newIntNodeT(ord(getFloat(a) <= getFloat(b)), n)
-  of mEqF64: result = newIntNodeT(ord(getFloat(a) == getFloat(b)), n)
-  of mLtStr: result = newIntNodeT(ord(getStr(a) < getStr(b)), n)
-  of mLeStr: result = newIntNodeT(ord(getStr(a) <= getStr(b)), n)
-  of mEqStr: result = newIntNodeT(ord(getStr(a) == getStr(b)), n)
+    result = newIntNodeT(ord(getOrdValue(a) == getOrdValue(b)), n, g)
+  of mLtF64: result = newIntNodeT(ord(getFloat(a) < getFloat(b)), n, g)
+  of mLeF64: result = newIntNodeT(ord(getFloat(a) <= getFloat(b)), n, g)
+  of mEqF64: result = newIntNodeT(ord(getFloat(a) == getFloat(b)), n, g)
+  of mLtStr: result = newIntNodeT(ord(getStr(a) < getStr(b)), n, g)
+  of mLeStr: result = newIntNodeT(ord(getStr(a) <= getStr(b)), n, g)
+  of mEqStr: result = newIntNodeT(ord(getStr(a) == getStr(b)), n, g)
   of mLtU, mLtU64:
-    result = newIntNodeT(ord(`<%`(getOrdValue(a), getOrdValue(b))), n)
+    result = newIntNodeT(ord(`<%`(getOrdValue(a), getOrdValue(b))), n, g)
   of mLeU, mLeU64:
-    result = newIntNodeT(ord(`<=%`(getOrdValue(a), getOrdValue(b))), n)
-  of mBitandI, mAnd: result = newIntNodeT(a.getInt and b.getInt, n)
-  of mBitorI, mOr: result = newIntNodeT(getInt(a) or getInt(b), n)
-  of mBitxorI, mXor: result = newIntNodeT(a.getInt xor b.getInt, n)
-  of mAddU: result = newIntNodeT(`+%`(getInt(a), getInt(b)), n)
-  of mSubU: result = newIntNodeT(`-%`(getInt(a), getInt(b)), n)
-  of mMulU: result = newIntNodeT(`*%`(getInt(a), getInt(b)), n)
-  of mModU:
-    let y = getInt(b)
-    if y != 0:
-      result = newIntNodeT(`%%`(getInt(a), y), n)
-  of mDivU:
-    let y = getInt(b)
-    if y != 0:
-      result = newIntNodeT(`/%`(getInt(a), y), n)
-  of mLeSet: result = newIntNodeT(ord(containsSets(a, b)), n)
-  of mEqSet: result = newIntNodeT(ord(equalSets(a, b)), n)
+    result = newIntNodeT(ord(`<=%`(getOrdValue(a), getOrdValue(b))), n, g)
+  of mBitandI, mAnd: result = newIntNodeT(a.getInt and b.getInt, n, g)
+  of mBitorI, mOr: result = newIntNodeT(getInt(a) or getInt(b), n, g)
+  of mBitxorI, mXor: result = newIntNodeT(a.getInt xor b.getInt, n, g)
+  of mAddU: result = newIntNodeT(`+%`(getInt(a), getInt(b)), n, g)
+  of mSubU: result = newIntNodeT(`-%`(getInt(a), getInt(b)), n, g)
+  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 mLtSet:
-    result = newIntNodeT(ord(containsSets(a, b) and not equalSets(a, b)), n)
+    result = newIntNodeT(ord(containsSets(a, b) and not equalSets(a, b)), n, g)
   of mMulSet:
     result = nimsets.intersectSets(a, b)
     result.info = n.info
@@ -282,125 +320,125 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
   of mSymDiffSet:
     result = nimsets.symdiffSets(a, b)
     result.info = n.info
-  of mConStrStr: result = newStrNodeT(getStrOrChar(a) & getStrOrChar(b), n)
-  of mInSet: result = newIntNodeT(ord(inSet(a, b)), n)
+  of mConStrStr: result = newStrNodeT(getStrOrChar(a) & getStrOrChar(b), n, g)
+  of mInSet: result = newIntNodeT(ord(inSet(a, b)), n, g)
   of mRepr:
     # BUGFIX: we cannot eval mRepr here for reasons that I forgot.
     discard
-  of mIntToStr, mInt64ToStr: result = newStrNodeT($(getOrdValue(a)), n)
+  of mIntToStr, mInt64ToStr: result = newStrNodeT($(getOrdValue(a)), n, g)
   of mBoolToStr:
-    if getOrdValue(a) == 0: result = newStrNodeT("false", n)
-    else: result = newStrNodeT("true", n)
-  of mCopyStr: result = newStrNodeT(substr(getStr(a), int(getOrdValue(b))), n)
+    if getOrdValue(a) == 0: result = newStrNodeT("false", n, g)
+    else: result = newStrNodeT("true", n, g)
+  of mCopyStr: result = newStrNodeT(substr(getStr(a), int(getOrdValue(b))), n, g)
   of mCopyStrLast:
     result = newStrNodeT(substr(getStr(a), int(getOrdValue(b)),
-                                           int(getOrdValue(c))), n)
-  of mFloatToStr: result = newStrNodeT($getFloat(a), n)
+                                           int(getOrdValue(c))), n, g)
+  of mFloatToStr: result = newStrNodeT($getFloat(a), n, g)
   of mCStrToStr, mCharToStr:
     if a.kind == nkBracket:
       var s = ""
       for b in a.sons:
         s.add b.getStrOrChar
-      result = newStrNodeT(s, n)
+      result = newStrNodeT(s, n, g)
     else:
-      result = newStrNodeT(getStrOrChar(a), n)
+      result = newStrNodeT(getStrOrChar(a), n, g)
   of mStrToStr: result = a
-  of mEnumToStr: result = newStrNodeT(ordinalValToString(a), n)
+  of mEnumToStr: result = newStrNodeT(ordinalValToString(a, g), n, g)
   of mArrToSeq:
     result = copyTree(a)
     result.typ = n.typ
   of mCompileOption:
-    result = newIntNodeT(ord(commands.testCompileOption(a.getStr, n.info)), n)
+    result = newIntNodeT(ord(commands.testCompileOption(g.config, a.getStr, n.info)), n, g)
   of mCompileOptionArg:
     result = newIntNodeT(ord(
-      testCompileOptionArg(getStr(a), getStr(b), n.info)), n)
+      testCompileOptionArg(g.config, getStr(a), getStr(b), n.info)), n, g)
   of mEqProc:
     result = newIntNodeT(ord(
-        exprStructuralEquivalent(a, b, strictSymEquality=true)), n)
+        exprStructuralEquivalent(a, b, strictSymEquality=true)), n, g)
   else: discard
 
-proc getConstIfExpr(c: PSym, n: PNode): PNode =
+proc getConstIfExpr(c: PSym, n: PNode; g: ModuleGraph): PNode =
   result = nil
   for i in countup(0, sonsLen(n) - 1):
     var it = n.sons[i]
     if it.len == 2:
-      var e = getConstExpr(c, it.sons[0])
+      var e = getConstExpr(c, it.sons[0], g)
       if e == nil: return nil
       if getOrdValue(e) != 0:
         if result == nil:
-          result = getConstExpr(c, it.sons[1])
+          result = getConstExpr(c, it.sons[1], g)
           if result == nil: return
     elif it.len == 1:
-      if result == nil: result = getConstExpr(c, it.sons[0])
-    else: internalError(it.info, "getConstIfExpr()")
+      if result == nil: result = getConstExpr(c, it.sons[0], g)
+    else: internalError(g.config, it.info, "getConstIfExpr()")
 
-proc leValueConv(a, b: PNode): bool =
+proc leValueConv*(a, b: PNode): bool =
   result = false
   case a.kind
   of nkCharLit..nkUInt64Lit:
     case b.kind
     of nkCharLit..nkUInt64Lit: result = a.intVal <= b.intVal
     of nkFloatLit..nkFloat128Lit: result = a.intVal <= round(b.floatVal).int
-    else: internalError(a.info, "leValueConv")
+    else: result = false #internalError(a.info, "leValueConv")
   of nkFloatLit..nkFloat128Lit:
     case b.kind
     of nkFloatLit..nkFloat128Lit: result = a.floatVal <= b.floatVal
     of nkCharLit..nkUInt64Lit: result = a.floatVal <= toFloat(int(b.intVal))
-    else: internalError(a.info, "leValueConv")
-  else: internalError(a.info, "leValueConv")
+    else: result = false # internalError(a.info, "leValueConv")
+  else: result = false # internalError(a.info, "leValueConv")
 
-proc magicCall(m: PSym, n: PNode): PNode =
+proc magicCall(m: PSym, n: PNode; g: ModuleGraph): PNode =
   if sonsLen(n) <= 1: return
 
   var s = n.sons[0].sym
-  var a = getConstExpr(m, n.sons[1])
+  var a = getConstExpr(m, n.sons[1], g)
   var b, c: PNode
   if a == nil: return
   if sonsLen(n) > 2:
-    b = getConstExpr(m, n.sons[2])
+    b = getConstExpr(m, n.sons[2], g)
     if b == nil: return
     if sonsLen(n) > 3:
-      c = getConstExpr(m, n.sons[3])
+      c = getConstExpr(m, n.sons[3], g)
       if c == nil: return
-  result = evalOp(s.magic, n, a, b, c)
-
-proc getAppType(n: PNode): PNode =
-  if gGlobalOptions.contains(optGenDynLib):
-    result = newStrNodeT("lib", n)
-  elif gGlobalOptions.contains(optGenStaticLib):
-    result = newStrNodeT("staticlib", n)
-  elif gGlobalOptions.contains(optGenGuiApp):
-    result = newStrNodeT("gui", n)
+  result = evalOp(s.magic, n, a, b, c, g)
+
+proc getAppType(n: PNode; g: ModuleGraph): PNode =
+  if g.config.globalOptions.contains(optGenDynLib):
+    result = newStrNodeT("lib", n, g)
+  elif g.config.globalOptions.contains(optGenStaticLib):
+    result = newStrNodeT("staticlib", n, g)
+  elif g.config.globalOptions.contains(optGenGuiApp):
+    result = newStrNodeT("gui", n, g)
   else:
-    result = newStrNodeT("console", n)
+    result = newStrNodeT("console", n, g)
 
-proc rangeCheck(n: PNode, value: BiggestInt) =
+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)
   else:
     err = value < firstOrd(n.typ) or value > lastOrd(n.typ)
   if err:
-    localError(n.info, errGenerated, "cannot convert " & $value &
+    localError(g.config, n.info, "cannot convert " & $value &
                                      " to " & typeToString(n.typ))
 
-proc foldConv*(n, a: PNode; check = false): PNode =
+proc foldConv*(n, a: PNode; g: ModuleGraph; check = false): PNode =
   # XXX range checks?
   case skipTypes(n.typ, abstractRange).kind
   of tyInt..tyInt64, tyUInt..tyUInt64:
     case skipTypes(a.typ, abstractRange).kind
     of tyFloat..tyFloat64:
-      result = newIntNodeT(int(getFloat(a)), n)
-    of tyChar: result = newIntNodeT(getOrdValue(a), n)
+      result = newIntNodeT(int(getFloat(a)), n, g)
+    of tyChar: result = newIntNodeT(getOrdValue(a), n, g)
     else:
       result = a
       result.typ = n.typ
     if check and result.kind in {nkCharLit..nkUInt64Lit}:
-      rangeCheck(n, result.intVal)
+      rangeCheck(n, result.intVal, g)
   of tyFloat..tyFloat64:
     case skipTypes(a.typ, abstractRange).kind
     of tyInt..tyInt64, tyEnum, tyBool, tyChar:
-      result = newFloatNodeT(toBiggestFloat(getOrdValue(a)), n)
+      result = newFloatNodeT(toBiggestFloat(getOrdValue(a)), n, g)
     else:
       result = a
       result.typ = n.typ
@@ -410,19 +448,19 @@ proc foldConv*(n, a: PNode; check = false): PNode =
     result = a
     result.typ = n.typ
 
-proc getArrayConstr(m: PSym, n: PNode): PNode =
+proc getArrayConstr(m: PSym, n: PNode; g: ModuleGraph): PNode =
   if n.kind == nkBracket:
     result = n
   else:
-    result = getConstExpr(m, n)
+    result = getConstExpr(m, n, g)
     if result == nil: result = n
 
-proc foldArrayAccess(m: PSym, n: PNode): PNode =
-  var x = getConstExpr(m, n.sons[0])
+proc foldArrayAccess(m: PSym, n: PNode; g: ModuleGraph): PNode =
+  var x = getConstExpr(m, n.sons[0], g)
   if x == nil or x.typ.skipTypes({tyGenericInst, tyAlias, tySink}).kind == tyTypeDesc:
     return
 
-  var y = getConstExpr(m, n.sons[1])
+  var y = getConstExpr(m, n.sons[1], g)
   if y == nil: return
 
   var idx = getOrdValue(y)
@@ -432,24 +470,24 @@ proc foldArrayAccess(m: PSym, n: PNode): PNode =
       result = x.sons[int(idx)]
       if result.kind == nkExprColonExpr: result = result.sons[1]
     else:
-      localError(n.info, errIndexOutOfBounds)
+      localError(g.config, n.info, "index out of bounds: " & $n)
   of nkBracket:
     idx = idx - x.typ.firstOrd
     if idx >= 0 and idx < x.len: result = x.sons[int(idx)]
-    else: localError(n.info, errIndexOutOfBounds)
+    else: localError(g.config, n.info, "index out of bounds: " & $n)
   of nkStrLit..nkTripleStrLit:
     result = newNodeIT(nkCharLit, x.info, n.typ)
     if idx >= 0 and idx < len(x.strVal):
       result.intVal = ord(x.strVal[int(idx)])
-    elif idx == len(x.strVal):
+    elif idx == len(x.strVal) and optLaxStrings in g.config.options:
       discard
     else:
-      localError(n.info, errIndexOutOfBounds)
+      localError(g.config, n.info, "index out of bounds: " & $n)
   else: discard
 
-proc foldFieldAccess(m: PSym, n: PNode): PNode =
+proc foldFieldAccess(m: PSym, n: PNode; g: ModuleGraph): PNode =
   # a real field access; proc calls have already been transformed
-  var x = getConstExpr(m, n.sons[0])
+  var x = getConstExpr(m, n.sons[0], g)
   if x == nil or x.kind notin {nkObjConstr, nkPar, nkTupleConstr}: return
 
   var field = n.sons[1].sym
@@ -463,13 +501,13 @@ proc foldFieldAccess(m: PSym, n: PNode): PNode =
     if it.sons[0].sym.name.id == field.name.id:
       result = x.sons[i].sons[1]
       return
-  localError(n.info, errFieldXNotFound, field.name.s)
+  localError(g.config, n.info, "field not found: " & field.name.s)
 
-proc foldConStrStr(m: PSym, n: PNode): PNode =
+proc foldConStrStr(m: PSym, n: PNode; g: ModuleGraph): PNode =
   result = newNodeIT(nkStrLit, n.info, n.typ)
   result.strVal = ""
   for i in countup(1, sonsLen(n) - 1):
-    let a = getConstExpr(m, n.sons[i])
+    let a = getConstExpr(m, n.sons[i], g)
     if a == nil: return nil
     result.strVal.add(getStrOrChar(a))
 
@@ -481,37 +519,55 @@ proc newSymNodeTypeDesc*(s: PSym; info: TLineInfo): PNode =
   else:
     result.typ = s.typ
 
-proc getConstExpr(m: PSym, n: PNode): PNode =
+proc getConstExpr(m: PSym, n: PNode; g: ModuleGraph): PNode =
   result = nil
+
+  proc getSrcTimestamp(): DateTime =
+    try:
+      result = utc(fromUnix(parseInt(getEnv("SOURCE_DATE_EPOCH",
+                                            "not a number"))))
+    except ValueError:
+      # Environment variable malformed.
+      # https://reproducible-builds.org/specs/source-date-epoch/: "If the
+      # value is malformed, the build process SHOULD exit with a non-zero
+      # error code", which this doesn't do. This uses local time, because
+      # that maintains compatibility with existing usage.
+      result = local(getTime())
+
   case n.kind
   of nkSym:
     var s = n.sym
     case s.kind
     of skEnumField:
-      result = newIntNodeT(s.position, n)
+      result = newIntNodeT(s.position, n, g)
     of skConst:
       case s.magic
-      of mIsMainModule: result = newIntNodeT(ord(sfMainModule in m.flags), n)
-      of mCompileDate: result = newStrNodeT(times.getDateStr(), n)
-      of mCompileTime: result = newStrNodeT(times.getClockStr(), n)
-      of mCpuEndian: result = newIntNodeT(ord(CPU[targetCPU].endian), n)
-      of mHostOS: result = newStrNodeT(toLowerAscii(platform.OS[targetOS].name), n)
-      of mHostCPU: result = newStrNodeT(platform.CPU[targetCPU].name.toLowerAscii, n)
-      of mBuildOS: result = newStrNodeT(toLowerAscii(platform.OS[platform.hostOS].name), n)
-      of mBuildCPU: result = newStrNodeT(platform.CPU[platform.hostCPU].name.toLowerAscii, n)
-      of mAppType: result = getAppType(n)
-      of mNaN: result = newFloatNodeT(NaN, n)
-      of mInf: result = newFloatNodeT(Inf, n)
-      of mNegInf: result = newFloatNodeT(NegInf, n)
+      of mIsMainModule: result = newIntNodeT(ord(sfMainModule in m.flags), n, g)
+      of mCompileDate: result = newStrNodeT(format(getSrcTimestamp(),
+                                                   "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 mAppType: result = getAppType(n, g)
+      of mNaN: result = newFloatNodeT(NaN, n, g)
+      of mInf: result = newFloatNodeT(Inf, n, g)
+      of mNegInf: result = newFloatNodeT(NegInf, n, g)
       of mIntDefine:
-        if isDefined(s.name):
-          result = newIntNodeT(lookupSymbol(s.name).parseInt, n)
+        if isDefined(g.config, s.name.s):
+          try:
+            result = newIntNodeT(g.config.symbols[s.name.s].parseInt, n, g)
+          except ValueError:
+            localError(g.config, n.info, "expression is not an integer literal")
       of mStrDefine:
-        if isDefined(s.name):
-          result = newStrNodeT(lookupSymbol(s.name), n)
+        if isDefined(g.config, s.name.s):
+          result = newStrNodeT(g.config.symbols[s.name.s], n, g)
       else:
         result = copyTree(s.ast)
-    of {skProc, skFunc, skMethod}:
+    of skProc, skFunc, skMethod:
       result = n
     of skType:
       # XXX gensym'ed symbols can come here and cannot be resolved. This is
@@ -531,7 +587,7 @@ proc getConstExpr(m: PSym, n: PNode): PNode =
   of nkCharLit..nkNilLit:
     result = copyNode(n)
   of nkIfExpr:
-    result = getConstIfExpr(m, n)
+    result = getConstIfExpr(m, n, g)
   of nkCallKinds:
     if n.sons[0].kind != nkSym: return
     var s = n.sons[0].sym
@@ -544,68 +600,67 @@ proc getConstExpr(m: PSym, n: PNode): PNode =
       of mSizeOf:
         var a = n.sons[1]
         if computeSize(a.typ) < 0:
-          localError(a.info, errCannotEvalXBecauseIncompletelyDefined,
-                     "sizeof")
+          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)
+          result = newIntNodeT(getSize(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)
+        result = newIntNodeT(firstOrd(n.sons[1].typ), n, g)
       of mHigh:
         if skipTypes(n.sons[1].typ, abstractVar).kind notin
             {tySequence, tyString, tyCString, tyOpenArray, tyVarargs}:
-          result = newIntNodeT(lastOrd(skipTypes(n[1].typ, abstractVar)), n)
+          result = newIntNodeT(lastOrd(skipTypes(n[1].typ, abstractVar)), n, g)
         else:
-          var a = getArrayConstr(m, n.sons[1])
+          var a = getArrayConstr(m, n.sons[1], g)
           if a.kind == nkBracket:
             # we can optimize it away:
-            result = newIntNodeT(sonsLen(a)-1, n)
+            result = newIntNodeT(sonsLen(a)-1, n, g)
       of mLengthOpenArray:
-        var a = getArrayConstr(m, n.sons[1])
+        var a = getArrayConstr(m, n.sons[1], g)
         if a.kind == nkBracket:
           # we can optimize it away! This fixes the bug ``len(134)``.
-          result = newIntNodeT(sonsLen(a), n)
+          result = newIntNodeT(sonsLen(a), n, g)
         else:
-          result = magicCall(m, n)
+          result = magicCall(m, n, g)
       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)
+        result = newIntNodeT(lengthOrd(n.sons[1].typ), n, g)
       of mAstToStr:
-        result = newStrNodeT(renderTree(n[1], {renderNoComments}), n)
+        result = newStrNodeT(renderTree(n[1], {renderNoComments}), n, g)
       of mConStrStr:
-        result = foldConStrStr(m, n)
+        result = foldConStrStr(m, n, g)
       of mIs:
-        let a = getConstExpr(m, n[1])
+        let a = getConstExpr(m, n[1], g)
         if a != nil and a.kind == nkSym and a.sym.kind == skType:
           result = evalIs(n, a)
       else:
-        result = magicCall(m, n)
+        result = magicCall(m, n, g)
     except OverflowError:
-      localError(n.info, errOverOrUnderflow)
+      localError(g.config, n.info, "over- or underflow")
     except DivByZeroError:
-      localError(n.info, errConstantDivisionByZero)
+      localError(g.config, n.info, "division by zero")
   of nkAddr:
-    var a = getConstExpr(m, n.sons[0])
+    var a = getConstExpr(m, n.sons[0], g)
     if a != nil:
       result = n
       n.sons[0] = a
   of nkBracket:
     result = copyTree(n)
     for i in countup(0, sonsLen(n) - 1):
-      var a = getConstExpr(m, n.sons[i])
+      var a = getConstExpr(m, n.sons[i], g)
       if a == nil: return nil
       result.sons[i] = a
     incl(result.flags, nfAllConst)
   of nkRange:
-    var a = getConstExpr(m, n.sons[0])
+    var a = getConstExpr(m, n.sons[0], g)
     if a == nil: return
-    var b = getConstExpr(m, n.sons[1])
+    var b = getConstExpr(m, n.sons[1], g)
     if b == nil: return
     result = copyNode(n)
     addSon(result, a)
@@ -613,7 +668,7 @@ proc getConstExpr(m: PSym, n: PNode): PNode =
   of nkCurly:
     result = copyTree(n)
     for i in countup(0, sonsLen(n) - 1):
-      var a = getConstExpr(m, n.sons[i])
+      var a = getConstExpr(m, n.sons[i], g)
       if a == nil: return nil
       result.sons[i] = a
     incl(result.flags, nfAllConst)
@@ -629,45 +684,45 @@ proc getConstExpr(m: PSym, n: PNode): PNode =
     result = copyTree(n)
     if (sonsLen(n) > 0) and (n.sons[0].kind == nkExprColonExpr):
       for i in countup(0, sonsLen(n) - 1):
-        var a = getConstExpr(m, n.sons[i].sons[1])
+        var a = getConstExpr(m, n.sons[i].sons[1], g)
         if a == nil: return nil
         result.sons[i].sons[1] = a
     else:
       for i in countup(0, sonsLen(n) - 1):
-        var a = getConstExpr(m, n.sons[i])
+        var a = getConstExpr(m, n.sons[i], g)
         if a == nil: return nil
         result.sons[i] = a
     incl(result.flags, nfAllConst)
   of nkChckRangeF, nkChckRange64, nkChckRange:
-    var a = getConstExpr(m, n.sons[0])
+    var a = getConstExpr(m, n.sons[0], g)
     if a == nil: return
     if leValueConv(n.sons[1], a) and leValueConv(a, n.sons[2]):
       result = a              # a <= x and x <= b
       result.typ = n.typ
     else:
-      localError(n.info, errGenerated, `%`(
-          msgKindToString(errIllegalConvFromXtoY),
-          [typeToString(n.sons[0].typ), typeToString(n.typ)]))
+      localError(g.config, n.info,
+        "conversion from $1 to $2 is invalid" %
+          [typeToString(n.sons[0].typ), typeToString(n.typ)])
   of nkStringToCString, nkCStringToString:
-    var a = getConstExpr(m, n.sons[0])
+    var a = getConstExpr(m, n.sons[0], g)
     if a == nil: return
     result = a
     result.typ = n.typ
   of nkHiddenStdConv, nkHiddenSubConv, nkConv:
-    var a = getConstExpr(m, n.sons[1])
+    var a = getConstExpr(m, n.sons[1], g)
     if a == nil: return
-    result = foldConv(n, a, check=n.kind == nkHiddenStdConv)
+    result = foldConv(n, a, g, check=n.kind == nkHiddenStdConv)
   of nkCast:
-    var a = getConstExpr(m, n.sons[1])
+    var a = getConstExpr(m, n.sons[1], g)
     if a == nil: return
     if n.typ != nil and n.typ.kind in NilableTypes:
       # we allow compile-time 'cast' for pointer types:
       result = a
       result.typ = n.typ
-  of nkBracketExpr: result = foldArrayAccess(m, n)
-  of nkDotExpr: result = foldFieldAccess(m, n)
+  of nkBracketExpr: result = foldArrayAccess(m, n, g)
+  of nkDotExpr: result = foldFieldAccess(m, n, g)
   of nkStmtListExpr:
     if n.len == 2 and n[0].kind == nkComesFrom:
-      result = getConstExpr(m, n[1])
+      result = getConstExpr(m, n[1], g)
   else:
     discard
diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim
index 16da06952..8f06e748e 100644
--- a/compiler/semgnrc.nim
+++ b/compiler/semgnrc.nim
@@ -17,13 +17,13 @@
 
 # included from sem.nim
 
-proc getIdentNode(n: PNode): PNode =
+proc getIdentNode(c: PContext; n: PNode): PNode =
   case n.kind
-  of nkPostfix: result = getIdentNode(n.sons[1])
-  of nkPragmaExpr: result = getIdentNode(n.sons[0])
+  of nkPostfix: result = getIdentNode(c, n.sons[1])
+  of nkPragmaExpr: result = getIdentNode(c, n.sons[0])
   of nkIdent, nkAccQuoted, nkSym: result = n
   else:
-    illFormedAst(n)
+    illFormedAst(n, c.config)
     result = n
 
 type
@@ -103,8 +103,8 @@ 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(n)
-  var s = searchInScopes(c, ident).skipAlias(n)
+  let ident = considerQuotedIdent(c.config, n)
+  var s = searchInScopes(c, ident).skipAlias(n, c.config)
   if s == nil:
     s = strTableGet(c.pureEnumFields, ident)
     if s != nil and contains(c.ambiguousSymbols, s.id):
@@ -140,8 +140,8 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags,
     n.sons[0] = semGenericStmt(c, n.sons[0], flags, ctx)
     result = n
     let n = n[1]
-    let ident = considerQuotedIdent(n)
-    var s = searchInScopes(c, ident).skipAlias(n)
+    let ident = considerQuotedIdent(c.config, 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:
@@ -158,7 +158,7 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags,
           result = newDot(result, syms)
 
 proc addTempDecl(c: PContext; n: PNode; kind: TSymKind) =
-  let s = newSymS(skUnknown, getIdentNode(n), c)
+  let s = newSymS(skUnknown, getIdentNode(c, n), c)
   addPrelimDecl(c, s)
   styleCheckDef(n.info, s, kind)
 
@@ -169,7 +169,7 @@ proc semGenericStmt(c: PContext, n: PNode,
   when defined(nimsuggest):
     if withinTypeDesc in flags: inc c.inTypeContext
 
-  #if gCmd == cmdIdeTools: suggestStmt(c, n)
+  #if conf.cmd == cmdIdeTools: suggestStmt(c, n)
   semIdeForTemplateOrGenericCheck(n, ctx.cursorInBody)
 
   case n.kind
@@ -201,13 +201,13 @@ proc semGenericStmt(c: PContext, n: PNode,
     result = semMixinStmt(c, n, ctx.toMixin)
   of nkCall, nkHiddenCallConv, nkInfix, nkPrefix, nkCommand, nkCallStrLit:
     # check if it is an expression macro:
-    checkMinSonsLen(n, 1)
+    checkMinSonsLen(n, 1, c.config)
     let fn = n.sons[0]
     var s = qualifiedLookUp(c, fn, {})
     if  s == nil and
         {withinMixin, withinConcept}*flags == {} and
         fn.kind in {nkIdent, nkAccQuoted} and
-        considerQuotedIdent(fn).id notin ctx.toMixin:
+        considerQuotedIdent(c.config, fn).id notin ctx.toMixin:
       errorUndeclaredIdentifier(c, n.info, fn.renderTree)
 
     var first = int ord(withinConcept in flags)
@@ -285,7 +285,7 @@ proc semGenericStmt(c: PContext, n: PNode,
     withBracketExpr ctx, n.sons[0]:
       result = semGenericStmt(c, result, flags, ctx)
   of nkAsgn, nkFastAsgn:
-    checkSonsLen(n, 2)
+    checkSonsLen(n, 2, c.config)
     let a = n.sons[0]
     let b = n.sons[1]
 
@@ -323,7 +323,7 @@ proc semGenericStmt(c: PContext, n: PNode,
     n.sons[0] = semGenericStmt(c, n.sons[0], flags, ctx)
     for i in countup(1, sonsLen(n)-1):
       var a = n.sons[i]
-      checkMinSonsLen(a, 1)
+      checkMinSonsLen(a, 1, c.config)
       var L = sonsLen(a)
       for j in countup(0, L-2):
         a.sons[j] = semGenericStmt(c, a.sons[j], flags, ctx)
@@ -340,23 +340,23 @@ proc semGenericStmt(c: PContext, n: PNode,
     closeScope(c)
     closeScope(c)
   of nkBlockStmt, nkBlockExpr, nkBlockType:
-    checkSonsLen(n, 2)
+    checkSonsLen(n, 2, c.config)
     openScope(c)
     if n.sons[0].kind != nkEmpty:
       addTempDecl(c, n.sons[0], skLabel)
     n.sons[1] = semGenericStmt(c, n.sons[1], flags, ctx)
     closeScope(c)
   of nkTryStmt:
-    checkMinSonsLen(n, 2)
+    checkMinSonsLen(n, 2, c.config)
     n.sons[0] = semGenericStmtScope(c, n.sons[0], flags, ctx)
     for i in countup(1, sonsLen(n)-1):
       var a = n.sons[i]
-      checkMinSonsLen(a, 1)
+      checkMinSonsLen(a, 1, c.config)
       var L = sonsLen(a)
       openScope(c)
       for j in countup(0, L-2):
         if a.sons[j].isInfixAs():
-          addTempDecl(c, getIdentNode(a.sons[j][2]), skLet)
+          addTempDecl(c, getIdentNode(c, a.sons[j][2]), skLet)
           a.sons[j].sons[1] = semGenericStmt(c, a.sons[j][1], flags+{withinTypeDesc}, ctx)
         else:
           a.sons[j] = semGenericStmt(c, a.sons[j], flags+{withinTypeDesc}, ctx)
@@ -367,44 +367,44 @@ proc semGenericStmt(c: PContext, n: PNode,
     for i in countup(0, sonsLen(n) - 1):
       var a = n.sons[i]
       if a.kind == nkCommentStmt: continue
-      if (a.kind != nkIdentDefs) and (a.kind != nkVarTuple): illFormedAst(a)
-      checkMinSonsLen(a, 3)
+      if (a.kind != nkIdentDefs) and (a.kind != nkVarTuple): illFormedAst(a, c.config)
+      checkMinSonsLen(a, 3, c.config)
       var L = sonsLen(a)
       a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc}, ctx)
       a.sons[L-1] = semGenericStmt(c, a.sons[L-1], flags, ctx)
       for j in countup(0, L-3):
-        addTempDecl(c, getIdentNode(a.sons[j]), skVar)
+        addTempDecl(c, getIdentNode(c, a.sons[j]), skVar)
   of nkGenericParams:
     for i in countup(0, sonsLen(n) - 1):
       var a = n.sons[i]
-      if (a.kind != nkIdentDefs): illFormedAst(a)
-      checkMinSonsLen(a, 3)
+      if (a.kind != nkIdentDefs): illFormedAst(a, c.config)
+      checkMinSonsLen(a, 3, c.config)
       var L = sonsLen(a)
       a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc}, ctx)
       # do not perform symbol lookup for default expressions
       for j in countup(0, L-3):
-        addTempDecl(c, getIdentNode(a.sons[j]), skType)
+        addTempDecl(c, getIdentNode(c, a.sons[j]), skType)
   of nkConstSection:
     for i in countup(0, sonsLen(n) - 1):
       var a = n.sons[i]
       if a.kind == nkCommentStmt: continue
-      if (a.kind != nkConstDef): illFormedAst(a)
-      checkSonsLen(a, 3)
-      addTempDecl(c, getIdentNode(a.sons[0]), skConst)
+      if (a.kind != nkConstDef): illFormedAst(a, c.config)
+      checkSonsLen(a, 3, c.config)
+      addTempDecl(c, getIdentNode(c, a.sons[0]), skConst)
       a.sons[1] = semGenericStmt(c, a.sons[1], flags+{withinTypeDesc}, ctx)
       a.sons[2] = semGenericStmt(c, a.sons[2], flags, ctx)
   of nkTypeSection:
     for i in countup(0, sonsLen(n) - 1):
       var a = n.sons[i]
       if a.kind == nkCommentStmt: continue
-      if (a.kind != nkTypeDef): illFormedAst(a)
-      checkSonsLen(a, 3)
-      addTempDecl(c, getIdentNode(a.sons[0]), skType)
+      if (a.kind != nkTypeDef): illFormedAst(a, c.config)
+      checkSonsLen(a, 3, c.config)
+      addTempDecl(c, getIdentNode(c, a.sons[0]), skType)
     for i in countup(0, sonsLen(n) - 1):
       var a = n.sons[i]
       if a.kind == nkCommentStmt: continue
-      if (a.kind != nkTypeDef): illFormedAst(a)
-      checkSonsLen(a, 3)
+      if (a.kind != nkTypeDef): illFormedAst(a, c.config)
+      checkSonsLen(a, 3, c.config)
       if a.sons[1].kind != nkEmpty:
         openScope(c)
         a.sons[1] = semGenericStmt(c, a.sons[1], flags, ctx)
@@ -421,28 +421,28 @@ proc semGenericStmt(c: PContext, n: PNode,
         case n.sons[i].kind
         of nkEnumFieldDef: a = n.sons[i].sons[0]
         of nkIdent: a = n.sons[i]
-        else: illFormedAst(n)
-        addDecl(c, newSymS(skUnknown, getIdentNode(a), c))
+        else: illFormedAst(n, c.config)
+        addDecl(c, newSymS(skUnknown, getIdentNode(c, a), c))
   of nkObjectTy, nkTupleTy, nkTupleClassTy:
     discard
   of nkFormalParams:
-    checkMinSonsLen(n, 1)
+    checkMinSonsLen(n, 1, c.config)
     if n.sons[0].kind != nkEmpty:
       n.sons[0] = semGenericStmt(c, n.sons[0], flags+{withinTypeDesc}, ctx)
     for i in countup(1, sonsLen(n) - 1):
       var a = n.sons[i]
-      if (a.kind != nkIdentDefs): illFormedAst(a)
-      checkMinSonsLen(a, 3)
+      if (a.kind != nkIdentDefs): illFormedAst(a, c.config)
+      checkMinSonsLen(a, 3, c.config)
       var L = sonsLen(a)
       a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc}, ctx)
       a.sons[L-1] = semGenericStmt(c, a.sons[L-1], flags, ctx)
       for j in countup(0, L-3):
-        addTempDecl(c, getIdentNode(a.sons[j]), skParam)
+        addTempDecl(c, getIdentNode(c, a.sons[j]), skParam)
   of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef,
      nkFuncDef, nkIteratorDef, nkLambdaKinds:
-    checkSonsLen(n, bodyPos + 1)
+    checkSonsLen(n, bodyPos + 1, c.config)
     if n.sons[namePos].kind != nkEmpty:
-      addTempDecl(c, getIdentNode(n.sons[0]), skProc)
+      addTempDecl(c, getIdentNode(c, n.sons[0]), skProc)
     openScope(c)
     n.sons[genericParamsPos] = semGenericStmt(c, n.sons[genericParamsPos],
                                               flags, ctx)
@@ -463,7 +463,7 @@ proc semGenericStmt(c: PContext, n: PNode,
     closeScope(c)
   of nkPragma, nkPragmaExpr: discard
   of nkExprColonExpr, nkExprEqExpr:
-    checkMinSonsLen(n, 2)
+    checkMinSonsLen(n, 2, c.config)
     result.sons[1] = semGenericStmt(c, n.sons[1], flags, ctx)
   else:
     for i in countup(0, sonsLen(n) - 1):
diff --git a/compiler/seminst.nim b/compiler/seminst.nim
index 32b385308..a5f0ca7d8 100644
--- a/compiler/seminst.nim
+++ b/compiler/seminst.nim
@@ -54,10 +54,13 @@ proc pushProcCon*(c: PContext; owner: PSym) =
   rawPushProcCon(c, owner)
   rawHandleSelf(c, owner)
 
+const
+  errCannotInstantiateX = "cannot instantiate: '$1'"
+
 iterator instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable): PSym =
-  internalAssert n.kind == nkGenericParams
+  internalAssert c.config, n.kind == nkGenericParams
   for i, a in n.pairs:
-    internalAssert a.kind == nkSym
+    internalAssert c.config, a.kind == nkSym
     var q = a.sym
     if q.typ.kind notin {tyTypeDesc, tyGenericParam, tyStatic}+tyTypeClasses:
       continue
@@ -71,10 +74,10 @@ iterator instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable): PSym
         # later by semAsgn in return type inference scenario
         t = q.typ
       else:
-        localError(a.info, errCannotInstantiateX, s.name.s)
+        localError(c.config, a.info, errCannotInstantiateX % s.name.s)
         t = errorType(c)
     elif t.kind == tyGenericParam:
-      localError(a.info, errCannotInstantiateX, q.name.s)
+      localError(c.config, a.info, errCannotInstantiateX % q.name.s)
       t = errorType(c)
     elif t.kind == tyGenericInvocation:
       #t = instGenericContainer(c, a, t)
@@ -145,7 +148,7 @@ proc instantiateBody(c: PContext, n, params: PNode, result, orig: PSym) =
     freshGenSyms(b, result, orig, symMap)
     b = semProcBody(c, b)
     b = hloBody(c, b)
-    n.sons[bodyPos] = transformBody(c.module, b, result)
+    n.sons[bodyPos] = transformBody(c.graph, c.module, b, result)
     #echo "code instantiated ", result.name.s
     excl(result.flags, sfForward)
     dec c.inGenericInst
@@ -174,7 +177,7 @@ proc sideEffectsCheck(c: PContext, s: PSym) =
 
 proc instGenericContainer(c: PContext, info: TLineInfo, header: PType,
                           allowMetaTypes = false): PType =
-  internalAssert header.kind == tyGenericInvocation
+  internalAssert c.config, header.kind == tyGenericInvocation
 
   var
     typeMap: LayeredIdTable
@@ -199,7 +202,7 @@ proc instGenericContainer(c: PContext, info: TLineInfo, header: PType,
     var param: PSym
 
     template paramSym(kind): untyped =
-        newSym(kind, genParam.sym.name, genericTyp.sym, genParam.sym.info)
+      newSym(kind, genParam.sym.name, genericTyp.sym, genParam.sym.info)
 
     if genParam.kind == tyStatic:
       param = paramSym skConst
@@ -233,14 +236,12 @@ 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)
   var typeMap = initLayeredTypeMap(pt)
   var cl = initTypeVars(c, addr(typeMap), info, nil)
   var result = instCopyType(cl, prc.typ)
   let originalParams = result.n
   result.n = originalParams.shallowCopy
-
   for i in 1 ..< result.len:
     # twrong_field_caching requires these 'resetIdTable' calls:
     if i > 1:
@@ -248,7 +249,7 @@ proc instantiateProcType(c: PContext, pt: TIdTable,
       resetIdTable(cl.localCache)
     result.sons[i] = replaceTypeVarsT(cl, result.sons[i])
     propagateToOwner(result, result.sons[i])
-    internalAssert originalParams[i].kind == nkSym
+    internalAssert c.config, originalParams[i].kind == nkSym
     when true:
       let oldParam = originalParams[i].sym
       let param = copySym(oldParam)
@@ -285,9 +286,9 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
   ## The `pt` parameter is a type-unsafe mapping table used to link generic
   ## parameters to their concrete types within the generic instance.
   # no need to instantiate generic templates/macros:
-  internalAssert fn.kind notin {skMacro, skTemplate}
+  internalAssert c.config, fn.kind notin {skMacro, skTemplate}
   # generates an instantiated proc
-  if c.instCounter > 1000: internalError(fn.ast.info, "nesting too deep")
+  if c.instCounter > 1000: internalError(c.config, fn.ast.info, "nesting too deep")
   inc(c.instCounter)
   # careful! we copy the whole AST including the possibly nil body!
   var n = copyTree(fn.ast)
@@ -306,7 +307,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
 
   openScope(c)
   let gp = n.sons[genericParamsPos]
-  internalAssert gp.kind != nkEmpty
+  internalAssert c.config, gp.kind != nkEmpty
   n.sons[namePos] = newSymNode(result)
   pushInfoContext(info)
   var entry = TInstantiation.new
diff --git a/compiler/semmacrosanity.nim b/compiler/semmacrosanity.nim
index f6df67441..02c56c035 100644
--- a/compiler/semmacrosanity.nim
+++ b/compiler/semmacrosanity.nim
@@ -10,7 +10,7 @@
 ## Implements type sanity checking for ASTs resulting from macros. Lots of
 ## room for improvement here.
 
-import ast, astalgo, msgs, types
+import ast, astalgo, msgs, types, options
 
 proc ithField(n: PNode, field: var int): PSym =
   result = nil
@@ -20,7 +20,7 @@ proc ithField(n: PNode, field: var int): PSym =
       result = ithField(n.sons[i], field)
       if result != nil: return
   of nkRecCase:
-    if n.sons[0].kind != nkSym: internalError(n.info, "ithField")
+    if n.sons[0].kind != nkSym: return
     result = ithField(n.sons[0], field)
     if result != nil: return
     for i in countup(1, sonsLen(n) - 1):
@@ -28,13 +28,13 @@ proc ithField(n: PNode, field: var int): PSym =
       of nkOfBranch, nkElse:
         result = ithField(lastSon(n.sons[i]), field)
         if result != nil: return
-      else: internalError(n.info, "ithField(record case branch)")
+      else: discard
   of nkSym:
     if field == 0: result = n.sym
     else: dec(field)
   else: discard
 
-proc annotateType*(n: PNode, t: PType) =
+proc annotateType*(n: PNode, t: PType; conf: ConfigRef) =
   let x = t.skipTypes(abstractInst+{tyRange})
   # Note: x can be unequal to t and we need to be careful to use 't'
   # to not to skip tyGenericInst
@@ -46,50 +46,50 @@ proc annotateType*(n: PNode, t: PType) =
       var j = i-1
       let field = x.n.ithField(j)
       if field.isNil:
-        globalError n.info, "invalid field at index " & $i
+        globalError conf, n.info, "invalid field at index " & $i
       else:
-        internalAssert(n.sons[i].kind == nkExprColonExpr)
-        annotateType(n.sons[i].sons[1], field.typ)
+        internalAssert(conf, n.sons[i].kind == nkExprColonExpr)
+        annotateType(n.sons[i].sons[1], field.typ, conf)
   of nkPar, nkTupleConstr:
     if x.kind == tyTuple:
       n.typ = t
       for i in 0 ..< n.len:
-        if i >= x.len: globalError n.info, "invalid field at index " & $i
-        else: annotateType(n.sons[i], x.sons[i])
+        if i >= x.len: globalError conf, n.info, "invalid field at index " & $i
+        else: annotateType(n.sons[i], x.sons[i], conf)
     elif x.kind == tyProc and x.callConv == ccClosure:
       n.typ = t
     else:
-      globalError(n.info, "() must have a tuple type")
+      globalError(conf, n.info, "() must have a tuple type")
   of nkBracket:
     if x.kind in {tyArray, tySequence, tyOpenArray}:
       n.typ = t
-      for m in n: annotateType(m, x.elemType)
+      for m in n: annotateType(m, x.elemType, conf)
     else:
-      globalError(n.info, "[] must have some form of array type")
+      globalError(conf, n.info, "[] must have some form of array type")
   of nkCurly:
     if x.kind in {tySet}:
       n.typ = t
-      for m in n: annotateType(m, x.elemType)
+      for m in n: annotateType(m, x.elemType, conf)
     else:
-      globalError(n.info, "{} must have the set type")
+      globalError(conf, n.info, "{} must have the set type")
   of nkFloatLit..nkFloat128Lit:
     if x.kind in {tyFloat..tyFloat128}:
       n.typ = t
     else:
-      globalError(n.info, "float literal must have some float type")
+      globalError(conf, n.info, "float literal must have some float type")
   of nkCharLit..nkUInt64Lit:
     if x.kind in {tyInt..tyUInt64, tyBool, tyChar, tyEnum}:
       n.typ = t
     else:
-      globalError(n.info, "integer literal must have some int type")
+      globalError(conf, n.info, "integer literal must have some int type")
   of nkStrLit..nkTripleStrLit:
     if x.kind in {tyString, tyCString}:
       n.typ = t
     else:
-      globalError(n.info, "string literal must be of some string type")
+      globalError(conf, n.info, "string literal must be of some string type")
   of nkNilLit:
     if x.kind in NilableTypes:
       n.typ = t
     else:
-      globalError(n.info, "nil literal must be of some pointer type")
+      globalError(conf, n.info, "nil literal must be of some pointer type")
   else: discard
diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim
index 3f0df0065..8515e971d 100644
--- a/compiler/semmagic.nim
+++ b/compiler/semmagic.nim
@@ -16,7 +16,7 @@ proc semAddr(c: PContext; n: PNode; isUnsafeAddr=false): PNode =
   if x.kind == nkSym:
     x.sym.flags.incl(sfAddrTaken)
   if isAssignable(c, x, isUnsafeAddr) notin {arLValue, arLocalLValue}:
-    localError(n.info, errExprHasNoAddress)
+    localError(c.config, n.info, errExprHasNoAddress)
   result.add x
   result.typ = makePtrType(c, x.typ)
 
@@ -43,7 +43,7 @@ proc semArrGet(c: PContext; n: PNode; flags: TExprFlags): PNode =
     let x = copyTree(n)
     x.sons[0] = newIdentNode(getIdent"[]", n.info)
     bracketNotFoundError(c, x)
-    #localError(n.info, "could not resolve: " & $n)
+    #localError(c.config, n.info, "could not resolve: " & $n)
     result = n
 
 proc semArrPut(c: PContext; n: PNode; flags: TExprFlags): PNode =
@@ -64,24 +64,24 @@ proc semAsgnOpr(c: PContext; n: PNode): PNode =
 
 proc semIsPartOf(c: PContext, n: PNode, flags: TExprFlags): PNode =
   var r = isPartOf(n[1], n[2])
-  result = newIntNodeT(ord(r), n)
+  result = newIntNodeT(ord(r), n, c.graph)
 
 proc expectIntLit(c: PContext, n: PNode): int =
   let x = c.semConstExpr(c, n)
   case x.kind
   of nkIntLit..nkInt64Lit: result = int(x.intVal)
-  else: localError(n.info, errIntLiteralExpected)
+  else: localError(c.config, n.info, errIntLiteralExpected)
 
 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)
-  var filename = newNodeIT(nkStrLit, n.info, getSysType(tyString))
+  var filename = newNodeIT(nkStrLit, n.info, getSysType(c.graph, n.info, tyString))
   filename.strVal = if useFullPaths != 0: info.toFullPath else: info.toFilename
-  var line = newNodeIT(nkIntLit, n.info, getSysType(tyInt))
+  var line = newNodeIT(nkIntLit, n.info, getSysType(c.graph, n.info, tyInt))
   line.intVal = toLinenumber(info)
-  var column = newNodeIT(nkIntLit, n.info, getSysType(tyInt))
+  var column = newNodeIT(nkIntLit, n.info, getSysType(c.graph, n.info, tyInt))
   column.intVal = toColumn(info)
   result.add(filename)
   result.add(line)
@@ -110,10 +110,10 @@ proc uninstantiate(t: PType): PType =
     of tyCompositeTypeClass: uninstantiate t.sons[1]
     else: t
 
-proc evalTypeTrait(traitCall: PNode, operand: PType, context: PSym): PNode =
+proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym): PNode =
   const skippedTypes = {tyTypeDesc, tyAlias, tySink}
   let trait = traitCall[0]
-  internalAssert trait.kind == nkSym
+  internalAssert c.config, trait.kind == nkSym
   var operand = operand.skipTypes(skippedTypes)
 
   template operand2: PType =
@@ -140,7 +140,7 @@ proc evalTypeTrait(traitCall: PNode, operand: PType, context: PSym): PNode =
   of "genericHead":
     var res = uninstantiate(operand)
     if res == operand and res.kind notin tyMagicGenerics:
-      localError(traitCall.info,
+      localError(c.config, traitCall.info,
         "genericHead expects a generic type. The given type was " &
         typeToString(operand))
       return newType(tyError, context).toNode(traitCall.info)
@@ -151,19 +151,19 @@ proc evalTypeTrait(traitCall: PNode, operand: PType, context: PSym): PNode =
     let t = operand.skipTypes({tyVar, tyLent, tyGenericInst, tyAlias, tySink, tyInferred})
     let complexObj = containsGarbageCollectedRef(t) or
                      hasDestructor(t)
-    result = newIntNodeT(ord(not complexObj), traitCall)
+    result = newIntNodeT(ord(not complexObj), traitCall, c.graph)
   else:
-    localError(traitCall.info, "unknown trait")
+    localError(c.config, traitCall.info, "unknown trait")
     result = emptyNode
 
 proc semTypeTraits(c: PContext, n: PNode): PNode =
-  checkMinSonsLen(n, 2)
+  checkMinSonsLen(n, 2, c.config)
   let t = n.sons[1].typ
-  internalAssert t != nil and t.kind == tyTypeDesc
+  internalAssert c.config, t != nil and t.kind == tyTypeDesc
   if t.sonsLen > 0:
     # This is either a type known to sem or a typedesc
     # param to a regular proc (again, known at instantiation)
-    result = evalTypeTrait(n, t, getCurrOwner(c))
+    result = evalTypeTrait(c, n, t, getCurrOwner(c))
   else:
     # a typedesc variable, pass unmodified to evals
     result = n
@@ -176,7 +176,7 @@ proc semOrd(c: PContext, n: PNode): PNode =
   elif parType.kind == tySet:
     result.typ = makeRangeType(c, firstOrd(parType), lastOrd(parType), n.info)
   else:
-    localError(n.info, errOrdinalTypeExpected)
+    localError(c.config, n.info, errOrdinalTypeExpected)
     result.typ = errorType(c)
 
 proc semBindSym(c: PContext, n: PNode): PNode =
@@ -185,13 +185,13 @@ proc semBindSym(c: PContext, n: PNode): PNode =
 
   let sl = semConstExpr(c, n.sons[1])
   if sl.kind notin {nkStrLit, nkRStrLit, nkTripleStrLit}:
-    localError(n.sons[1].info, errStringLiteralExpected)
+    localError(c.config, n.sons[1].info, errStringLiteralExpected)
     return errorNode(c, n)
 
   let isMixin = semConstExpr(c, n.sons[2])
   if isMixin.kind != nkIntLit or isMixin.intVal < 0 or
       isMixin.intVal > high(TSymChoiceRule).int:
-    localError(n.sons[2].info, errConstExprExpected)
+    localError(c.config, n.sons[2].info, errConstExprExpected)
     return errorNode(c, n)
 
   let id = newIdentNode(getIdent(sl.strVal), n.info)
@@ -199,6 +199,10 @@ proc semBindSym(c: PContext, n: PNode): PNode =
   if s != nil:
     # we need to mark all symbols:
     var sc = symChoice(c, id, s, TSymChoiceRule(isMixin.intVal))
+    if not (c.inStaticContext > 0 or getCurrOwner(c).isCompileTimeProc):
+      # inside regular code, bindSym resolves to the sym-choice
+      # nodes (see tinspectsymbol)
+      return sc
     result.add(sc)
   else:
     errorUndeclaredIdentifier(c, n.sons[1].info, sl.strVal)
@@ -221,9 +225,9 @@ proc semOf(c: PContext, n: PNode): PNode =
     let y = skipTypes(n.sons[2].typ, abstractPtrs-{tyTypeDesc})
 
     if x.kind == tyTypeDesc or y.kind != tyTypeDesc:
-      localError(n.info, errXExpectsObjectTypes, "of")
+      localError(c.config, n.info, "'of' takes object types")
     elif b.kind != tyObject or a.kind != tyObject:
-      localError(n.info, errXExpectsObjectTypes, "of")
+      localError(c.config, n.info, "'of' takes object types")
     else:
       let diff = inheritanceDiff(a, b)
       # | returns: 0 iff `a` == `b`
@@ -232,26 +236,26 @@ proc semOf(c: PContext, n: PNode): PNode =
       # | returns: `maxint` iff `a` and `b` are not compatible at all
       if diff <= 0:
         # optimize to true:
-        message(n.info, hintConditionAlwaysTrue, renderTree(n))
+        message(c.config, n.info, hintConditionAlwaysTrue, renderTree(n))
         result = newIntNode(nkIntLit, 1)
         result.info = n.info
-        result.typ = getSysType(tyBool)
+        result.typ = getSysType(c.graph, n.info, tyBool)
         return result
       elif diff == high(int):
-        localError(n.info, errXcanNeverBeOfThisSubtype, typeToString(a))
+        localError(c.config, n.info, "'$1' cannot be of this subtype" % typeToString(a))
   else:
-    localError(n.info, errXExpectsTwoArguments, "of")
-  n.typ = getSysType(tyBool)
+    localError(c.config, n.info, "'of' takes 2 arguments")
+  n.typ = getSysType(c.graph, n.info, tyBool)
   result = n
 
 proc magicsAfterOverloadResolution(c: PContext, n: PNode,
                                    flags: TExprFlags): PNode =
   case n[0].sym.magic
   of mAddr:
-    checkSonsLen(n, 2)
+    checkSonsLen(n, 2, c.config)
     result = semAddr(c, n.sons[1], n[0].sym.name.s == "unsafeAddr")
   of mTypeOf:
-    checkSonsLen(n, 2)
+    checkSonsLen(n, 2, c.config)
     result = semTypeOf(c, n.sons[1])
   of mArrGet: result = semArrGet(c, n, flags)
   of mArrPut: result = semArrPut(c, n, flags)
@@ -263,8 +267,8 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
   of mIsPartOf: result = semIsPartOf(c, n, flags)
   of mTypeTrait: result = semTypeTraits(c, n)
   of mAstToStr:
-    result = newStrNodeT(renderTree(n[1], {renderNoComments}), n)
-    result.typ = getSysType(tyString)
+    result = newStrNodeT(renderTree(n[1], {renderNoComments}), n, c.graph)
+    result.typ = getSysType(c.graph, n.info, tyString)
   of mInstantiationInfo: result = semInstantiationInfo(c, n)
   of mOrd: result = semOrd(c, n)
   of mOf: result = semOf(c, n)
@@ -277,11 +281,11 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
   of mDotDot:
     result = n
   of mRoof:
-    localError(n.info, "builtin roof operator is not supported anymore")
+    localError(c.config, n.info, "builtin roof operator is not supported anymore")
   of mPlugin:
     let plugin = getPlugin(n[0].sym)
     if plugin.isNil:
-      localError(n.info, "cannot find plugin " & n[0].sym.name.s)
+      localError(c.config, n.info, "cannot find plugin " & n[0].sym.name.s)
       result = n
     else:
       result = plugin(c, n)
diff --git a/compiler/semobjconstr.nim b/compiler/semobjconstr.nim
index cfa0fbd05..1e0802953 100644
--- a/compiler/semobjconstr.nim
+++ b/compiler/semobjconstr.nim
@@ -39,32 +39,32 @@ proc mergeInitStatus(existing: var InitStatus, newStatus: InitStatus) =
   of initUnknown:
     discard
 
-proc invalidObjConstr(n: PNode) =
+proc invalidObjConstr(c: PContext, n: PNode) =
   if n.kind == nkInfix and n[0].kind == nkIdent and n[0].ident.s[0] == ':':
-    localError(n.info, "incorrect object construction syntax; use a space after the colon")
+    localError(c.config, n.info, "incorrect object construction syntax; use a space after the colon")
   else:
-    localError(n.info, "incorrect object construction syntax")
+    localError(c.config, n.info, "incorrect object construction syntax")
 
-proc locateFieldInInitExpr(field: PSym, initExpr: PNode): PNode =
+proc locateFieldInInitExpr(c: PContext, field: PSym, initExpr: PNode): PNode =
   # Returns the assignment nkExprColonExpr node or nil
   let fieldId = field.name.id
   for i in 1 ..< initExpr.len:
     let assignment = initExpr[i]
     if assignment.kind != nkExprColonExpr:
-      invalidObjConstr(assignment)
+      invalidObjConstr(c, assignment)
       continue
 
-    if fieldId == considerQuotedIdent(assignment[0]).id:
+    if fieldId == considerQuotedIdent(c.config, assignment[0]).id:
       return assignment
 
 proc semConstrField(c: PContext, flags: TExprFlags,
                     field: PSym, initExpr: PNode): PNode =
-  let assignment = locateFieldInInitExpr(field, initExpr)
+  let assignment = locateFieldInInitExpr(c, field, initExpr)
   if assignment != nil:
     if nfSem in assignment.flags: return assignment[1]
     if not fieldVisible(c, field):
-      localError(initExpr.info,
-        "the field '$1' is not accessible.", [field.name.s])
+      localError(c.config, initExpr.info,
+        "the field '$1' is not accessible." % [field.name.s])
       return
 
     var initValue = semExprFlagDispatched(c, assignment[1], flags)
@@ -101,25 +101,25 @@ iterator directFieldsInRecList(recList: PNode): PNode =
   if recList.kind == nkSym:
     yield recList
   else:
-    internalAssert recList.kind == nkRecList
+    doAssert recList.kind == nkRecList
     for field in recList:
       if field.kind != nkSym: continue
       yield field
 
 template quoteStr(s: string): string = "'" & s & "'"
 
-proc fieldsPresentInInitExpr(fieldsRecList, initExpr: PNode): string =
+proc fieldsPresentInInitExpr(c: PContext, fieldsRecList, initExpr: PNode): string =
   result = ""
   for field in directFieldsInRecList(fieldsRecList):
-    let assignment = locateFieldInInitExpr(field.sym, initExpr)
+    let assignment = locateFieldInInitExpr(c, field.sym, initExpr)
     if assignment != nil:
       if result.len != 0: result.add ", "
       result.add field.sym.name.s.quoteStr
 
-proc missingMandatoryFields(fieldsRecList, initExpr: PNode): string =
+proc missingMandatoryFields(c: PContext, fieldsRecList, initExpr: PNode): string =
   for r in directFieldsInRecList(fieldsRecList):
     if {tfNotNil, tfNeedsInit} * r.sym.typ.flags != {}:
-      let assignment = locateFieldInInitExpr(r.sym, initExpr)
+      let assignment = locateFieldInInitExpr(c, r.sym, initExpr)
       if assignment == nil:
         if result == nil:
           result = r.sym.name.s
@@ -127,10 +127,10 @@ proc missingMandatoryFields(fieldsRecList, initExpr: PNode): string =
           result.add ", "
           result.add r.sym.name.s
 
-proc checkForMissingFields(recList, initExpr: PNode) =
-  let missing = missingMandatoryFields(recList, initExpr)
+proc checkForMissingFields(c: PContext, recList, initExpr: PNode) =
+  let missing = missingMandatoryFields(c, recList, initExpr)
   if missing != nil:
-    localError(initExpr.info, "fields not initialized: $1.", [missing])
+    localError(c.config, initExpr.info, "fields not initialized: $1.", [missing])
 
 proc semConstructFields(c: PContext, recNode: PNode,
                         initExpr: PNode, flags: TExprFlags): InitStatus =
@@ -146,14 +146,14 @@ proc semConstructFields(c: PContext, recNode: PNode,
     template fieldsPresentInBranch(branchIdx: int): string =
       let branch = recNode[branchIdx]
       let fields = branch[branch.len - 1]
-      fieldsPresentInInitExpr(fields, initExpr)
+      fieldsPresentInInitExpr(c, fields, initExpr)
 
     template checkMissingFields(branchNode: PNode) =
       let fields = branchNode[branchNode.len - 1]
-      checkForMissingFields(fields, initExpr)
+      checkForMissingFields(c, fields, initExpr)
 
-    let discriminator = recNode.sons[0];
-    internalAssert discriminator.kind == nkSym
+    let discriminator = recNode.sons[0]
+    internalAssert c.config, discriminator.kind == nkSym
     var selectedBranch = -1
 
     for i in 1 ..< recNode.len:
@@ -164,9 +164,9 @@ proc semConstructFields(c: PContext, recNode: PNode,
         if selectedBranch != -1:
           let prevFields = fieldsPresentInBranch(selectedBranch)
           let currentFields = fieldsPresentInBranch(i)
-          localError(initExpr.info,
-            "The fields ($1) and ($2) cannot be initialized together, " &
-            "because they are from conflicting branches in the case object.",
+          localError(c.config, initExpr.info,
+            ("The fields '$1' and '$2' cannot be initialized together, " &
+            "because they are from conflicting branches in the case object.") %
             [prevFields, currentFields])
           result = initConflict
         else:
@@ -179,9 +179,9 @@ proc semConstructFields(c: PContext, recNode: PNode,
                                             discriminator.sym, initExpr)
       if discriminatorVal == nil:
         let fields = fieldsPresentInBranch(selectedBranch)
-        localError(initExpr.info,
-          "you must provide a compile-time value for the discriminator '$1' " &
-          "in order to prove that it's safe to initialize $2.",
+        localError(c.config, initExpr.info,
+          ("you must provide a compile-time value for the discriminator '$1' " &
+          "in order to prove that it's safe to initialize $2.") %
           [discriminator.sym.name.s, fields])
         mergeInitStatus(result, initNone)
       else:
@@ -189,7 +189,7 @@ proc semConstructFields(c: PContext, recNode: PNode,
 
         template wrongBranchError(i) =
           let fields = fieldsPresentInBranch(i)
-          localError(initExpr.info,
+          localError(c.config, initExpr.info,
             "a case selecting discriminator '$1' with value '$2' " &
             "appears in the object construction, but the field(s) $3 " &
             "are in conflict with this value.",
@@ -219,7 +219,7 @@ proc semConstructFields(c: PContext, recNode: PNode,
         # value was given to the discrimator. We can assume that it will be
         # initialized to zero and this will select a particular branch as
         # a result:
-        let matchedBranch = recNode.pickCaseBranch newIntLit(0)
+        let matchedBranch = recNode.pickCaseBranch newIntLit(c.graph, initExpr.info, 0)
         checkMissingFields matchedBranch
       else:
         result = initPartial
@@ -239,20 +239,17 @@ proc semConstructFields(c: PContext, recNode: PNode,
     result = if e != nil: initFull else: initNone
 
   else:
-    internalAssert false
+    internalAssert c.config, false
 
 proc semConstructType(c: PContext, initExpr: PNode,
                       t: PType, flags: TExprFlags): InitStatus =
   var t = t
   result = initUnknown
-
   while true:
     let status = semConstructFields(c, t.n, initExpr, flags)
     mergeInitStatus(result, status)
-
     if status in {initPartial, initNone, initUnknown}:
-      checkForMissingFields t.n, initExpr
-
+      checkForMissingFields c, t.n, initExpr
     let base = t.sons[0]
     if base == nil: break
     t = skipTypes(base, skipPtrs)
@@ -263,13 +260,13 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
   for child in n: result.add child
 
   if t == nil:
-    localError(n.info, errGenerated, "object constructor needs an object type")
+    localError(c.config, n.info, errGenerated, "object constructor needs an object type")
     return
 
   t = skipTypes(t, {tyGenericInst, tyAlias, tySink})
   if t.kind == tyRef: t = skipTypes(t.sons[0], {tyGenericInst, tyAlias, tySink})
   if t.kind != tyObject:
-    localError(n.info, errGenerated, "object constructor needs an object type")
+    localError(c.config, n.info, errGenerated, "object constructor needs an object type")
     return
 
   # Check if the object is fully initialized by recursively testing each
@@ -296,17 +293,16 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
     let field = result[i]
     if nfSem notin field.flags:
       if field.kind != nkExprColonExpr:
-        invalidObjConstr(field)
+        invalidObjConstr(c, field)
         continue
-      let id = considerQuotedIdent(field[0])
+      let id = considerQuotedIdent(c.config, 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(result[j][0])
+        let prevId = considerQuotedIdent(c.config, result[j][0])
         if prevId.id == id.id:
-          localError(field.info, errFieldInitTwice, id.s)
+          localError(c.config, field.info, errFieldInitTwice % id.s)
           return
       # 2) No such field exists in the constructed type
-      localError(field.info, errUndeclaredFieldX, id.s)
+      localError(c.config, field.info, errUndeclaredFieldX % id.s)
       return
-
diff --git a/compiler/semparallel.nim b/compiler/semparallel.nim
index e12fd84bb..ea5aab628 100644
--- a/compiler/semparallel.nim
+++ b/compiler/semparallel.nim
@@ -23,7 +23,8 @@
 
 import
   ast, astalgo, idents, lowerings, magicsys, guards, sempass2, msgs,
-  renderer, types
+  renderer, types, modulegraphs, options
+
 from trees import getMagic
 from strutils import `%`
 
@@ -73,12 +74,15 @@ type
                         # the 'parallel' section
     currentSpawnId: int
     inLoop: int
+    graph: ModuleGraph
 
-proc initAnalysisCtx(): AnalysisCtx =
+proc initAnalysisCtx(g: ModuleGraph): AnalysisCtx =
   result.locals = @[]
   result.slices = @[]
   result.args = @[]
-  result.guards = @[]
+  result.guards.s = @[]
+  result.guards.o = initOperators(g)
+  result.graph = g
 
 proc lookupSlot(c: AnalysisCtx; s: PSym): int =
   for i in 0..<c.locals.len:
@@ -117,7 +121,7 @@ proc checkLocal(c: AnalysisCtx; n: PNode) =
   if isLocal(n):
     let s = c.lookupSlot(n.sym)
     if s >= 0 and c.locals[s].stride != nil:
-      localError(n.info, "invalid usage of counter after increment")
+      localError(c.graph.config, n.info, "invalid usage of counter after increment")
   else:
     for i in 0 ..< n.safeLen: checkLocal(c, n.sons[i])
 
@@ -126,14 +130,14 @@ template `?`(x): untyped = x.renderTree
 proc checkLe(c: AnalysisCtx; a, b: PNode) =
   case proveLe(c.guards, a, b)
   of impUnknown:
-    localError(a.info, "cannot prove: " & ?a & " <= " & ?b & " (bounds check)")
+    localError(c.graph.config, a.info, "cannot prove: " & ?a & " <= " & ?b & " (bounds check)")
   of impYes: discard
   of impNo:
-    localError(a.info, "can prove: " & ?a & " > " & ?b & " (bounds check)")
+    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)
+  checkLe(c, idx, arr.highBound(c.guards.o))
 
 proc addLowerBoundAsFacts(c: var AnalysisCtx) =
   for v in c.locals:
@@ -142,34 +146,34 @@ proc addLowerBoundAsFacts(c: var AnalysisCtx) =
 
 proc addSlice(c: var AnalysisCtx; n: PNode; x, le, ri: PNode) =
   checkLocal(c, n)
-  let le = le.canon
-  let ri = ri.canon
+  let le = le.canon(c.guards.o)
+  let ri = ri.canon(c.guards.o)
   # perform static bounds checking here; and not later!
-  let oldState = c.guards.len
+  let oldState = c.guards.s.len
   addLowerBoundAsFacts(c)
   c.checkBounds(x, le)
   c.checkBounds(x, ri)
-  c.guards.setLen(oldState)
+  c.guards.s.setLen(oldState)
   c.slices.add((x, le, ri, c.currentSpawnId, c.inLoop > 0))
 
-proc overlap(m: TModel; x,y,c,d: PNode) =
+proc overlap(m: TModel; conf: ConfigRef; x,y,c,d: PNode) =
   #  X..Y and C..D overlap iff (X <= D and C <= Y)
   case proveLe(m, c, y)
   of impUnknown:
     case proveLe(m, x, d)
     of impNo: discard
     of impUnknown, impYes:
-      localError(x.info,
+      localError(conf, x.info,
         "cannot prove: $# > $#; required for ($#)..($#) disjoint from ($#)..($#)" %
             [?c, ?y, ?x, ?y, ?c, ?d])
   of impYes:
     case proveLe(m, x, d)
     of impUnknown:
-      localError(x.info,
+      localError(conf, x.info,
         "cannot prove: $# > $#; required for ($#)..($#) disjoint from ($#)..($#)" %
           [?x, ?d, ?x, ?y, ?c, ?d])
     of impYes:
-      localError(x.info, "($#)..($#) not disjoint from ($#)..($#)" %
+      localError(conf, x.info, "($#)..($#) not disjoint from ($#)..($#)" %
                 [?c, ?y, ?x, ?y, ?c, ?d])
     of impNo: discard
   of impNo: discard
@@ -187,7 +191,7 @@ proc subStride(c: AnalysisCtx; n: PNode): PNode =
   if isLocal(n):
     let s = c.lookupSlot(n.sym)
     if s >= 0 and c.locals[s].stride != nil:
-      result = n +@ c.locals[s].stride.intVal
+      result = buildAdd(n, c.locals[s].stride.intVal, c.guards.o)
     else:
       result = n
   elif n.safeLen > 0:
@@ -203,7 +207,7 @@ proc checkSlicesAreDisjoint(c: var AnalysisCtx) =
   addLowerBoundAsFacts(c)
   # Every slice used in a loop needs to be disjoint with itself:
   for x,a,b,id,inLoop in items(c.slices):
-    if inLoop: overlap(c.guards, a,b, c.subStride(a), c.subStride(b))
+    if inLoop: overlap(c.guards, c.graph.config, a,b, c.subStride(a), c.subStride(b))
   # Another tricky example is:
   #   while true:
   #     spawn f(a[i])
@@ -231,21 +235,21 @@ proc checkSlicesAreDisjoint(c: var AnalysisCtx) =
           # XXX strictly speaking, 'or' is not correct here and it needs to
           # be 'and'. However this prevents too many obviously correct programs
           # like f(a[0..x]); for i in x+1 .. a.high: f(a[i])
-          overlap(c.guards, x.a, x.b, y.a, y.b)
+          overlap(c.guards, c.graph.config, x.a, x.b, y.a, y.b)
         elif (let k = simpleSlice(x.a, x.b); let m = simpleSlice(y.a, y.b);
               k >= 0 and m >= 0):
           # ah I cannot resist the temptation and add another sweet heuristic:
           # if both slices have the form (i+k)..(i+k)  and (i+m)..(i+m) we
           # check they are disjoint and k < stride and m < stride:
-          overlap(c.guards, x.a, x.b, y.a, y.b)
+          overlap(c.guards, c.graph.config, x.a, x.b, y.a, y.b)
           let stride = min(c.stride(x.a), c.stride(y.a))
           if k < stride and m < stride:
             discard
           else:
-            localError(x.x.info, "cannot prove ($#)..($#) disjoint from ($#)..($#)" %
+            localError(c.graph.config, x.x.info, "cannot prove ($#)..($#) disjoint from ($#)..($#)" %
               [?x.a, ?x.b, ?y.a, ?y.b])
         else:
-          localError(x.x.info, "cannot prove ($#)..($#) disjoint from ($#)..($#)" %
+          localError(c.graph.config, x.x.info, "cannot prove ($#)..($#) disjoint from ($#)..($#)" %
             [?x.a, ?x.b, ?y.a, ?y.b])
 
 proc analyse(c: var AnalysisCtx; n: PNode)
@@ -292,31 +296,31 @@ proc analyseCall(c: var AnalysisCtx; n: PNode; op: PSym) =
 
 proc analyseCase(c: var AnalysisCtx; n: PNode) =
   analyse(c, n.sons[0])
-  let oldFacts = c.guards.len
+  let oldFacts = c.guards.s.len
   for i in 1..<n.len:
     let branch = n.sons[i]
-    setLen(c.guards, oldFacts)
+    setLen(c.guards.s, oldFacts)
     addCaseBranchFacts(c.guards, n, i)
     for i in 0 ..< branch.len:
       analyse(c, branch.sons[i])
-  setLen(c.guards, oldFacts)
+  setLen(c.guards.s, oldFacts)
 
 proc analyseIf(c: var AnalysisCtx; n: PNode) =
   analyse(c, n.sons[0].sons[0])
-  let oldFacts = c.guards.len
-  addFact(c.guards, canon(n.sons[0].sons[0]))
+  let oldFacts = c.guards.s.len
+  addFact(c.guards, canon(n.sons[0].sons[0], c.guards.o))
 
   analyse(c, n.sons[0].sons[1])
   for i in 1..<n.len:
     let branch = n.sons[i]
-    setLen(c.guards, oldFacts)
+    setLen(c.guards.s, oldFacts)
     for j in 0..i-1:
-      addFactNeg(c.guards, canon(n.sons[j].sons[0]))
+      addFactNeg(c.guards, canon(n.sons[j].sons[0], c.guards.o))
     if branch.len > 1:
-      addFact(c.guards, canon(branch.sons[0]))
+      addFact(c.guards, canon(branch.sons[0], c.guards.o))
     for i in 0 ..< branch.len:
       analyse(c, branch.sons[i])
-  setLen(c.guards, oldFacts)
+  setLen(c.guards.s, oldFacts)
 
 proc analyse(c: var AnalysisCtx; n: PNode) =
   case n.kind
@@ -349,7 +353,7 @@ proc analyse(c: var AnalysisCtx; n: PNode) =
       c.addSlice(n, n[0], n[1], n[1])
     analyseSons(c, n)
   of nkReturnStmt, nkRaiseStmt, nkTryStmt:
-    localError(n.info, "invalid control flow for 'parallel'")
+    localError(c.graph.config, n.info, "invalid control flow for 'parallel'")
     # 'break' that leaves the 'parallel' section is not valid either
     # or maybe we should generate a 'try' XXX
   of nkVarSection, nkLetSection:
@@ -365,7 +369,7 @@ proc analyse(c: var AnalysisCtx; n: PNode) =
           if it[j].isLocal:
             let slot = c.getSlot(it[j].sym)
             if slot.lower.isNil: slot.lower = value
-            else: internalError(it.info, "slot already has a lower bound")
+            else: internalError(c.graph.config, it.info, "slot already has a lower bound")
         if not isSpawned: analyse(c, value)
   of nkCaseStmt: analyseCase(c, n)
   of nkWhen, nkIfStmt, nkIfExpr: analyseIf(c, n)
@@ -378,14 +382,14 @@ proc analyse(c: var AnalysisCtx; n: PNode) =
     else:
       # loop may never execute:
       let oldState = c.locals.len
-      let oldFacts = c.guards.len
-      addFact(c.guards, canon(n.sons[0]))
+      let oldFacts = c.guards.s.len
+      addFact(c.guards, canon(n.sons[0], c.guards.o))
       analyse(c, n.sons[1])
       setLen(c.locals, oldState)
-      setLen(c.guards, oldFacts)
+      setLen(c.guards.s, oldFacts)
       # we know after the loop the negation holds:
       if not hasSubnodeWith(n.sons[1], nkBreakStmt):
-        addFactNeg(c.guards, canon(n.sons[0]))
+        addFactNeg(c.guards, canon(n.sons[0], c.guards.o))
     dec c.inLoop
   of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef,
       nkMacroDef, nkTemplateDef, nkConstSection, nkPragma, nkFuncDef:
@@ -393,13 +397,13 @@ proc analyse(c: var AnalysisCtx; n: PNode) =
   else:
     analyseSons(c, n)
 
-proc transformSlices(n: PNode): PNode =
+proc transformSlices(g: ModuleGraph; n: PNode): PNode =
   if n.kind in nkCallKinds and n[0].kind == nkSym:
     let op = n[0].sym
     if op.name.s == "[]" and op.fromSystem:
       result = copyNode(n)
-      let opSlice = newSymNode(createMagic("slice", mSlice))
-      opSlice.typ = getSysType(tyInt)
+      let opSlice = newSymNode(createMagic(g, "slice", mSlice))
+      opSlice.typ = getSysType(g, n.info, tyInt)
       result.add opSlice
       result.add n[1]
       let slice = n[2].skipStmtList
@@ -409,49 +413,49 @@ proc transformSlices(n: PNode): PNode =
   if n.safeLen > 0:
     result = shallowCopy(n)
     for i in 0 ..< n.len:
-      result.sons[i] = transformSlices(n.sons[i])
+      result.sons[i] = transformSlices(g, n.sons[i])
   else:
     result = n
 
-proc transformSpawn(owner: PSym; n, barrier: PNode): PNode
-proc transformSpawnSons(owner: PSym; n, barrier: PNode): PNode =
+proc transformSpawn(g: ModuleGraph; owner: PSym; n, barrier: PNode): PNode
+proc transformSpawnSons(g: ModuleGraph; owner: PSym; n, barrier: PNode): PNode =
   result = shallowCopy(n)
   for i in 0 ..< n.len:
-    result.sons[i] = transformSpawn(owner, n.sons[i], barrier)
+    result.sons[i] = transformSpawn(g, owner, n.sons[i], barrier)
 
-proc transformSpawn(owner: PSym; n, barrier: PNode): PNode =
+proc transformSpawn(g: ModuleGraph; owner: PSym; n, barrier: PNode): PNode =
   case n.kind
   of nkVarSection, nkLetSection:
     result = nil
     for it in n:
       let b = it.lastSon
       if getMagic(b) == mSpawn:
-        if it.len != 3: localError(it.info, "invalid context for 'spawn'")
-        let m = transformSlices(b)
+        if it.len != 3: localError(g.config, it.info, "invalid context for 'spawn'")
+        let m = transformSlices(g, b)
         if result.isNil:
           result = newNodeI(nkStmtList, n.info)
           result.add n
         let t = b[1][0].typ.sons[0]
         if spawnResult(t, true) == srByVar:
-          result.add wrapProcForSpawn(owner, m, b.typ, barrier, it[0])
+          result.add wrapProcForSpawn(g, owner, m, b.typ, barrier, it[0])
           it.sons[it.len-1] = emptyNode
         else:
-          it.sons[it.len-1] = wrapProcForSpawn(owner, m, b.typ, barrier, nil)
+          it.sons[it.len-1] = wrapProcForSpawn(g, owner, m, b.typ, barrier, nil)
     if result.isNil: result = n
   of nkAsgn, nkFastAsgn:
     let b = n[1]
     if getMagic(b) == mSpawn and (let t = b[1][0].typ.sons[0];
         spawnResult(t, true) == srByVar):
-      let m = transformSlices(b)
-      return wrapProcForSpawn(owner, m, b.typ, barrier, n[0])
-    result = transformSpawnSons(owner, n, barrier)
+      let m = transformSlices(g, b)
+      return wrapProcForSpawn(g, owner, m, b.typ, barrier, n[0])
+    result = transformSpawnSons(g, owner, n, barrier)
   of nkCallKinds:
     if getMagic(n) == mSpawn:
-      result = transformSlices(n)
-      return wrapProcForSpawn(owner, result, n.typ, barrier, nil)
-    result = transformSpawnSons(owner, n, barrier)
+      result = transformSlices(g, n)
+      return wrapProcForSpawn(g, owner, result, n.typ, barrier, nil)
+    result = transformSpawnSons(g, owner, n, barrier)
   elif n.safeLen > 0:
-    result = transformSpawnSons(owner, n, barrier)
+    result = transformSpawnSons(g, owner, n, barrier)
   else:
     result = n
 
@@ -461,7 +465,7 @@ proc checkArgs(a: var AnalysisCtx; n: PNode) =
 proc generateAliasChecks(a: AnalysisCtx; result: PNode) =
   discard "too implement"
 
-proc liftParallel*(owner: PSym; n: PNode): PNode =
+proc liftParallel*(g: ModuleGraph; owner: PSym; n: PNode): PNode =
   # this needs to be called after the 'for' loop elimination
 
   # first pass:
@@ -470,17 +474,17 @@ proc liftParallel*(owner: PSym; n: PNode): PNode =
   # - detect used arguments
   #echo "PAR ", renderTree(n)
 
-  var a = initAnalysisCtx()
+  var a = initAnalysisCtx(g)
   let body = n.lastSon
   analyse(a, body)
   if a.spawns == 0:
-    localError(n.info, "'parallel' section without 'spawn'")
+    localError(g.config, n.info, "'parallel' section without 'spawn'")
   checkSlicesAreDisjoint(a)
   checkArgs(a, body)
 
   var varSection = newNodeI(nkVarSection, n.info)
   var temp = newSym(skTemp, getIdent"barrier", owner, n.info)
-  temp.typ = magicsys.getCompilerProc("Barrier").typ
+  temp.typ = magicsys.getCompilerProc(g, "Barrier").typ
   incl(temp.flags, sfFromGeneric)
   let tempNode = newSymNode(temp)
   varSection.addVar tempNode
@@ -489,6 +493,6 @@ proc liftParallel*(owner: PSym; n: PNode): PNode =
   result = newNodeI(nkStmtList, n.info)
   generateAliasChecks(a, result)
   result.add varSection
-  result.add callCodegenProc("openBarrier", barrier)
-  result.add transformSpawn(owner, body, barrier)
-  result.add callCodegenProc("closeBarrier", barrier)
+  result.add callCodegenProc(g, "openBarrier", barrier)
+  result.add transformSpawn(g, owner, body, barrier)
+  result.add callCodegenProc(g, "closeBarrier", barrier)
diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim
index d2a10f714..b66d7d9f2 100644
--- a/compiler/sempass2.nim
+++ b/compiler/sempass2.nim
@@ -9,7 +9,8 @@
 
 import
   intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees,
-  wordrecg, strutils, options, guards, writetracking
+  wordrecg, strutils, options, guards, writetracking, configuration,
+  modulegraphs
 
 when defined(useDfa):
   import dfa
@@ -52,6 +53,8 @@ type
     locked: seq[PNode] # locked locations
     gcUnsafe, isRecursive, isToplevel, hasSideEffect, inEnforcedGcSafe: bool
     maxLockLevel, currLockLevel: TLockLevel
+    config: ConfigRef
+    graph: ModuleGraph
   PEffects = var TEffects
 
 proc `<`(a, b: TLockLevel): bool {.borrow.}
@@ -72,24 +75,23 @@ proc getLockLevel(t: PType): TLockLevel =
 
 proc lockLocations(a: PEffects; pragma: PNode) =
   if pragma.kind != nkExprColonExpr:
-    localError(pragma.info, errGenerated, "locks pragma without argument")
+    localError(a.config, pragma.info, "locks pragma without argument")
     return
   var firstLL = TLockLevel(-1'i16)
   for x in pragma[1]:
     let thisLL = getLockLevel(x.typ)
     if thisLL != 0.TLockLevel:
       if thisLL < 0.TLockLevel or thisLL > MaxLockLevel.TLockLevel:
-        localError(x.info, "invalid lock level: " & $thisLL)
+        localError(a.config, x.info, "invalid lock level: " & $thisLL)
       elif firstLL < 0.TLockLevel: firstLL = thisLL
       elif firstLL != thisLL:
-        localError(x.info, errGenerated,
+        localError(a.config, x.info,
           "multi-lock requires the same static lock level for every operand")
       a.maxLockLevel = max(a.maxLockLevel, firstLL)
     a.locked.add x
   if firstLL >= 0.TLockLevel and firstLL != a.currLockLevel:
     if a.currLockLevel > 0.TLockLevel and a.currLockLevel <= firstLL:
-      localError(pragma.info, errGenerated,
-        "invalid nested locking")
+      localError(a.config, pragma.info, "invalid nested locking")
     a.currLockLevel = firstLL
 
 proc guardGlobal(a: PEffects; n: PNode; guard: PSym) =
@@ -102,7 +104,7 @@ proc guardGlobal(a: PEffects; n: PNode; guard: PSym) =
   #  message(n.info, warnUnguardedAccess, renderTree(n))
   #else:
   if not a.isTopLevel:
-    localError(n.info, errGenerated, "unguarded access: " & renderTree(n))
+    localError(a.config, n.info, "unguarded access: " & renderTree(n))
 
 # 'guard*' are checks which are concerned with 'guard' annotations
 # (var x{.guard: y.}: int)
@@ -125,7 +127,7 @@ proc guardDotAccess(a: PEffects; n: PNode) =
         if ty == nil: break
         ty = ty.skipTypes(skipPtrs)
     if field == nil:
-      localError(n.info, errGenerated, "invalid guard field: " & g.name.s)
+      localError(a.config, n.info, "invalid guard field: " & g.name.s)
       return
     g = field
     #ri.sym.guard = field
@@ -138,13 +140,13 @@ proc guardDotAccess(a: PEffects; n: PNode) =
     for L in a.locked:
       #if a.guards.sameSubexprs(dot, L): return
       if guards.sameTree(dot, L): return
-    localError(n.info, errGenerated, "unguarded access: " & renderTree(n))
+    localError(a.config, n.info, "unguarded access: " & renderTree(n))
   else:
     guardGlobal(a, n, g)
 
 proc makeVolatile(a: PEffects; s: PSym) {.inline.} =
   template compileToCpp(a): untyped =
-    gCmd == cmdCompileToCpp or sfCompileToCpp in getModule(a.owner).flags
+    a.config.cmd == cmdCompileToCpp or sfCompileToCpp in getModule(a.owner).flags
   if a.inTryStmt > 0 and not compileToCpp(a):
     incl(s.flags, sfVolatile)
 
@@ -167,9 +169,9 @@ proc initVarViaNew(a: PEffects, n: PNode) =
   elif isLocalVar(a, s):
     makeVolatile(a, s)
 
-proc warnAboutGcUnsafe(n: PNode) =
+proc warnAboutGcUnsafe(n: PNode; conf: ConfigRef) =
   #assert false
-  message(n.info, warnGcUnsafe, renderTree(n))
+  message(conf, n.info, warnGcUnsafe, renderTree(n))
 
 proc markGcUnsafe(a: PEffects; reason: PSym) =
   if not a.inEnforcedGcSafe:
@@ -184,7 +186,7 @@ proc markGcUnsafe(a: PEffects; reason: PNode) =
         a.owner.gcUnsafetyReason = reason.sym
       else:
         a.owner.gcUnsafetyReason = newSym(skUnknown, getIdent("<unknown>"),
-                                          a.owner, reason.info)
+                                          a.owner, reason.info, {})
 
 when true:
   template markSideEffect(a: PEffects; reason: typed) =
@@ -194,42 +196,42 @@ else:
     a.hasSideEffect = true
     markGcUnsafe(a, reason)
 
-proc listGcUnsafety(s: PSym; onlyWarning: bool; cycleCheck: var IntSet) =
+proc listGcUnsafety(s: PSym; onlyWarning: bool; cycleCheck: var IntSet; conf: ConfigRef) =
   let u = s.gcUnsafetyReason
   if u != nil and not cycleCheck.containsOrIncl(u.id):
     let msgKind = if onlyWarning: warnGcUnsafe2 else: errGenerated
     case u.kind
     of skLet, skVar:
-      message(s.info, msgKind,
+      message(conf, s.info, msgKind,
         ("'$#' is not GC-safe as it accesses '$#'" &
         " which is a global using GC'ed memory") % [s.name.s, u.name.s])
     of routineKinds:
       # recursive call *always* produces only a warning so the full error
       # message is printed:
-      listGcUnsafety(u, true, cycleCheck)
-      message(s.info, msgKind,
+      listGcUnsafety(u, true, cycleCheck, conf)
+      message(conf, s.info, msgKind,
         "'$#' is not GC-safe as it calls '$#'" %
         [s.name.s, u.name.s])
     of skParam, skForVar:
-      message(s.info, msgKind,
+      message(conf, s.info, msgKind,
         "'$#' is not GC-safe as it performs an indirect call via '$#'" %
         [s.name.s, u.name.s])
     else:
-      message(u.info, msgKind,
+      message(conf, u.info, msgKind,
         "'$#' is not GC-safe as it performs an indirect call here" % s.name.s)
 
-proc listGcUnsafety(s: PSym; onlyWarning: bool) =
+proc listGcUnsafety(s: PSym; onlyWarning: bool; conf: ConfigRef) =
   var cycleCheck = initIntSet()
-  listGcUnsafety(s, onlyWarning, cycleCheck)
+  listGcUnsafety(s, onlyWarning, cycleCheck, conf)
 
 proc useVar(a: PEffects, n: PNode) =
   let s = n.sym
   if isLocalVar(a, s):
     if s.id notin a.init:
       if {tfNeedsInit, tfNotNil} * s.typ.flags != {}:
-        message(n.info, warnProveInit, s.name.s)
+        message(a.config, n.info, warnProveInit, s.name.s)
       else:
-        message(n.info, warnUninit, s.name.s)
+        message(a.config, n.info, warnUninit, s.name.s)
       # prevent superfluous warnings about the same variable:
       a.init.add s.id
   if {sfGlobal, sfThread} * s.flags != {} and s.kind in {skVar, skLet} and
@@ -257,34 +259,30 @@ proc addToIntersection(inter: var TIntersection, s: int) =
 proc throws(tracked, n: PNode) =
   if n.typ == nil or n.typ.kind != tyError: tracked.add n
 
-proc getEbase(): PType =
-  result = if getCompilerProc("Exception") != nil: sysTypeFromName"Exception"
-           else: sysTypeFromName"E_Base"
+proc getEbase(g: ModuleGraph; info: TLineInfo): PType =
+  result = g.sysTypeFromName(info, "Exception")
 
-proc excType(n: PNode): PType =
+proc excType(g: ModuleGraph; n: PNode): PType =
   # reraise is like raising E_Base:
-  let t = if n.kind == nkEmpty or n.typ.isNil: getEbase() else: n.typ
+  let t = if n.kind == nkEmpty or n.typ.isNil: getEbase(g, n.info) else: n.typ
   result = skipTypes(t, skipPtrs)
 
-proc createRaise(n: PNode): PNode =
+proc createRaise(g: ModuleGraph; n: PNode): PNode =
   result = newNode(nkType)
-  result.typ = getEbase()
+  result.typ = getEbase(g, n.info)
   if not n.isNil: result.info = n.info
 
-proc createTag(n: PNode): PNode =
+proc createTag(g: ModuleGraph; n: PNode): PNode =
   result = newNode(nkType)
-  if getCompilerProc("RootEffect") != nil:
-    result.typ = sysTypeFromName"RootEffect"
-  else:
-    result.typ = sysTypeFromName"TEffect"
+  result.typ = g.sysTypeFromName(n.info, "RootEffect")
   if not n.isNil: result.info = n.info
 
 proc addEffect(a: PEffects, e: PNode, useLineInfo=true) =
   assert e.kind != nkRaiseStmt
   var aa = a.exc
   for i in a.bottom ..< aa.len:
-    if sameType(aa[i].excType, e.excType):
-      if not useLineInfo or gCmd == cmdDoc: return
+    if sameType(a.graph.excType(aa[i]), a.graph.excType(e)):
+      if not useLineInfo or a.config.cmd == cmdDoc: return
       elif aa[i].info == e.info: return
   throws(a.exc, e)
 
@@ -292,25 +290,25 @@ proc addTag(a: PEffects, e: PNode, useLineInfo=true) =
   var aa = a.tags
   for i in 0 ..< aa.len:
     if sameType(aa[i].typ.skipTypes(skipPtrs), e.typ.skipTypes(skipPtrs)):
-      if not useLineInfo or gCmd == cmdDoc: return
+      if not useLineInfo or a.config.cmd == cmdDoc: return
       elif aa[i].info == e.info: return
   throws(a.tags, e)
 
 proc mergeEffects(a: PEffects, b, comesFrom: PNode) =
   if b.isNil:
-    addEffect(a, createRaise(comesFrom))
+    addEffect(a, createRaise(a.graph, comesFrom))
   else:
     for effect in items(b): addEffect(a, effect, useLineInfo=comesFrom != nil)
 
 proc mergeTags(a: PEffects, b, comesFrom: PNode) =
   if b.isNil:
-    addTag(a, createTag(comesFrom))
+    addTag(a, createTag(a.graph, comesFrom))
   else:
     for effect in items(b): addTag(a, effect, useLineInfo=comesFrom != nil)
 
 proc listEffects(a: PEffects) =
-  for e in items(a.exc):  message(e.info, hintUser, typeToString(e.typ))
-  for e in items(a.tags): message(e.info, hintUser, typeToString(e.typ))
+  for e in items(a.exc):  message(a.config, e.info, hintUser, typeToString(e.typ))
+  for e in items(a.tags): message(a.config, e.info, hintUser, typeToString(e.typ))
   #if a.maxLockLevel != 0:
   #  message(e.info, hintUser, "lockLevel: " & a.maxLockLevel)
 
@@ -320,7 +318,7 @@ proc catches(tracked: PEffects, e: PType) =
   var i = tracked.bottom
   while i < L:
     # r supertype of e?
-    if safeInheritanceDiff(tracked.exc[i].excType, e) <= 0:
+    if safeInheritanceDiff(tracked.graph.excType(tracked.exc[i]), e) <= 0:
       tracked.exc.sons[i] = tracked.exc.sons[L-1]
       dec L
     else:
@@ -489,7 +487,7 @@ proc mergeLockLevels(tracked: PEffects, n: PNode, lockLevel: TLockLevel) =
   if lockLevel >= tracked.currLockLevel:
     # if in lock section:
     if tracked.currLockLevel > 0.TLockLevel:
-      localError n.info, errGenerated,
+      localError tracked.config, n.info, errGenerated,
         "expected lock level < " & $tracked.currLockLevel &
         " but got lock level " & $lockLevel
     tracked.maxLockLevel = max(tracked.maxLockLevel, lockLevel)
@@ -503,22 +501,22 @@ proc propagateEffects(tracked: PEffects, n: PNode, s: PSym) =
   mergeTags(tracked, tagSpec, n)
 
   if notGcSafe(s.typ) and sfImportc notin s.flags:
-    if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n)
+    if warnGcUnsafe in tracked.config.notes: warnAboutGcUnsafe(n, tracked.config)
     markGcUnsafe(tracked, s)
   if tfNoSideEffect notin s.typ.flags:
     markSideEffect(tracked, s)
   mergeLockLevels(tracked, n, s.getLockLevel)
 
-proc procVarcheck(n: PNode) =
+proc procVarcheck(n: PNode; conf: ConfigRef) =
   if n.kind in nkSymChoices:
-    for x in n: procVarCheck(x)
+    for x in n: procVarCheck(x, conf)
   elif n.kind == nkSym and n.sym.magic != mNone and n.sym.kind in routineKinds:
-    localError(n.info, errXCannotBePassedToProcVar, n.sym.name.s)
+    localError(conf, n.info, "'$1' cannot be passed to a procvar" % n.sym.name.s)
 
 proc notNilCheck(tracked: PEffects, n: PNode, paramType: PType) =
   let n = n.skipConv
   if paramType.isNil or paramType.kind != tyTypeDesc:
-    procVarcheck skipConvAndClosure(n)
+    procVarcheck skipConvAndClosure(n), tracked.config
   #elif n.kind in nkSymChoices:
   #  echo "came here"
   let paramType = paramType.skipTypesOrNil(abstractInst)
@@ -534,15 +532,16 @@ proc notNilCheck(tracked: PEffects, n: PNode, paramType: PType) =
       return
     case impliesNotNil(tracked.guards, n)
     of impUnknown:
-      message(n.info, errGenerated,
+      message(tracked.config, n.info, errGenerated,
               "cannot prove '$1' is not nil" % n.renderTree)
     of impNo:
-      message(n.info, errGenerated, "'$1' is provably nil" % n.renderTree)
+      message(tracked.config, n.info, errGenerated,
+              "'$1' is provably nil" % n.renderTree)
     of impYes: discard
 
 proc assumeTheWorst(tracked: PEffects; n: PNode; op: PType) =
-  addEffect(tracked, createRaise(n))
-  addTag(tracked, createTag(n))
+  addEffect(tracked, createRaise(tracked.graph, n))
+  addTag(tracked, createTag(tracked.graph, n))
   let lockLevel = if op.lockLevel == UnspecifiedLockLevel: UnknownLockLevel
                   else: op.lockLevel
   #if lockLevel == UnknownLockLevel:
@@ -557,7 +556,7 @@ proc trackOperand(tracked: PEffects, n: PNode, paramType: PType) =
   let a = skipConvAndClosure(n)
   let op = a.typ
   if op != nil and op.kind == tyProc and n.skipConv.kind != nkNilLit:
-    internalAssert op.n.sons[0].kind == nkEffectList
+    internalAssert tracked.config, op.n.sons[0].kind == nkEffectList
     var effectList = op.n.sons[0]
     let s = n.skipConv
     if s.kind == nkSym and s.sym.kind in routineKinds:
@@ -572,7 +571,7 @@ proc trackOperand(tracked: PEffects, n: PNode, paramType: PType) =
         assumeTheWorst(tracked, n, op)
       # assume GcUnsafe unless in its type; 'forward' does not matter:
       if notGcSafe(op) and not isOwnedProcVar(a, tracked.owner):
-        if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n)
+        if warnGcUnsafe in tracked.config.notes: warnAboutGcUnsafe(n, tracked.config)
         markGcUnsafe(tracked, a)
       elif tfNoSideEffect notin op.flags and not isOwnedProcVar(a, tracked.owner):
         markSideEffect(tracked, a)
@@ -580,7 +579,7 @@ proc trackOperand(tracked: PEffects, n: PNode, paramType: PType) =
       mergeEffects(tracked, effectList.sons[exceptionEffects], n)
       mergeTags(tracked, effectList.sons[tagEffects], n)
       if notGcSafe(op):
-        if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n)
+        if warnGcUnsafe in tracked.config.notes: warnAboutGcUnsafe(n, tracked.config)
         markGcUnsafe(tracked, a)
       elif tfNoSideEffect notin op.flags:
         markSideEffect(tracked, a)
@@ -592,7 +591,7 @@ proc trackOperand(tracked: PEffects, n: PNode, paramType: PType) =
     # XXX figure out why this can be a non tyProc here. See httpclient.nim for an
     # example that triggers it.
     if argtype.kind == tyProc and notGcSafe(argtype) and not tracked.inEnforcedGcSafe:
-      localError(n.info, $n & " is not GC safe")
+      localError(tracked.config, n.info, $n & " is not GC safe")
   notNilCheck(tracked, n, paramType)
 
 proc breaksBlock(n: PNode): bool =
@@ -608,18 +607,18 @@ proc breaksBlock(n: PNode): bool =
 proc trackCase(tracked: PEffects, n: PNode) =
   track(tracked, n.sons[0])
   let oldState = tracked.init.len
-  let oldFacts = tracked.guards.len
+  let oldFacts = tracked.guards.s.len
   let stringCase = skipTypes(n.sons[0].typ,
         abstractVarRange-{tyTypeDesc}).kind in {tyFloat..tyFloat128, tyString}
   let interesting = not stringCase and interestingCaseExpr(n.sons[0]) and
-        warnProveField in gNotes
+        warnProveField in tracked.config.notes
   var inter: TIntersection = @[]
   var toCover = 0
   for i in 1..<n.len:
     let branch = n.sons[i]
     setLen(tracked.init, oldState)
     if interesting:
-      setLen(tracked.guards, oldFacts)
+      setLen(tracked.guards.s, oldFacts)
       addCaseBranchFacts(tracked.guards, n, i)
     for i in 0 ..< branch.len:
       track(tracked, branch.sons[i])
@@ -632,11 +631,11 @@ proc trackCase(tracked: PEffects, n: PNode) =
     for id, count in items(inter):
       if count >= toCover: tracked.init.add id
     # else we can't merge
-  setLen(tracked.guards, oldFacts)
+  setLen(tracked.guards.s, oldFacts)
 
 proc trackIf(tracked: PEffects, n: PNode) =
   track(tracked, n.sons[0].sons[0])
-  let oldFacts = tracked.guards.len
+  let oldFacts = tracked.guards.s.len
   addFact(tracked.guards, n.sons[0].sons[0])
   let oldState = tracked.init.len
 
@@ -649,7 +648,7 @@ proc trackIf(tracked: PEffects, n: PNode) =
 
   for i in 1..<n.len:
     let branch = n.sons[i]
-    setLen(tracked.guards, oldFacts)
+    setLen(tracked.guards.s, oldFacts)
     for j in 0..i-1:
       addFactNeg(tracked.guards, n.sons[j].sons[0])
     if branch.len > 1:
@@ -665,7 +664,7 @@ proc trackIf(tracked: PEffects, n: PNode) =
     for id, count in items(inter):
       if count >= toCover: tracked.init.add id
     # else we can't merge as it is not exhaustive
-  setLen(tracked.guards, oldFacts)
+  setLen(tracked.guards.s, oldFacts)
 
 proc trackBlock(tracked: PEffects, n: PNode) =
   if n.kind in {nkStmtList, nkStmtListExpr}:
@@ -693,7 +692,7 @@ proc paramType(op: PType, i: int): PType =
 proc cstringCheck(tracked: PEffects; n: PNode) =
   if n.sons[0].typ.kind == tyCString and (let a = skipConv(n[1]);
       a.typ.kind == tyString and a.kind notin {nkStrLit..nkTripleStrLit}):
-    message(n.info, warnUnsafeCode, renderTree(n))
+    message(tracked.config, n.info, warnUnsafeCode, renderTree(n))
 
 proc track(tracked: PEffects, n: PNode) =
   case n.kind
@@ -735,7 +734,7 @@ proc track(tracked: PEffects, n: PNode) =
         if notGcSafe(op) and not importedFromC(a):
           # and it's not a recursive call:
           if not (a.kind == nkSym and a.sym == tracked.owner):
-            if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n)
+            if warnGcUnsafe in tracked.config.notes: warnAboutGcUnsafe(n, tracked.config)
             markGcUnsafe(tracked, a)
         if tfNoSideEffect notin op.flags and not importedFromC(a):
           # and it's not a recursive call:
@@ -753,7 +752,7 @@ proc track(tracked: PEffects, n: PNode) =
           # var s: seq[notnil];  newSeq(s, 0)  is a special case!
           discard
         else:
-          message(arg.info, warnProveInit, $arg)
+          message(tracked.config, arg.info, warnProveInit, $arg)
     for i in 0 ..< safeLen(n):
       track(tracked, n.sons[i])
   of nkDotExpr:
@@ -761,7 +760,8 @@ proc track(tracked: PEffects, n: PNode) =
     for i in 0 ..< len(n): track(tracked, n.sons[i])
   of nkCheckedFieldExpr:
     track(tracked, n.sons[0])
-    if warnProveField in gNotes: checkFieldAccess(tracked.guards, n)
+    if warnProveField in tracked.config.notes:
+      checkFieldAccess(tracked.guards, n, tracked.config)
   of nkTryStmt: trackTryStmt(tracked, n)
   of nkPragma: trackPragmaStmt(tracked, n)
   of nkAsgn, nkFastAsgn:
@@ -798,11 +798,11 @@ proc track(tracked: PEffects, n: PNode) =
     else:
       # loop may never execute:
       let oldState = tracked.init.len
-      let oldFacts = tracked.guards.len
+      let oldFacts = tracked.guards.s.len
       addFact(tracked.guards, n.sons[0])
       track(tracked, n.sons[1])
       setLen(tracked.init, oldState)
-      setLen(tracked.guards, oldFacts)
+      setLen(tracked.guards.s, oldFacts)
   of nkForStmt, nkParForStmt:
     # we are very conservative here and assume the loop is never executed:
     let oldState = tracked.init.len
@@ -811,13 +811,13 @@ proc track(tracked: PEffects, n: PNode) =
     setLen(tracked.init, oldState)
   of nkObjConstr:
     when false: track(tracked, n.sons[0])
-    let oldFacts = tracked.guards.len
+    let oldFacts = tracked.guards.s.len
     for i in 1 ..< len(n):
       let x = n.sons[i]
       track(tracked, x)
       if x.sons[0].kind == nkSym and sfDiscriminant in x.sons[0].sym.flags:
         addDiscriminantFact(tracked.guards, x)
-    setLen(tracked.guards, oldFacts)
+    setLen(tracked.guards.s, oldFacts)
   of nkPragmaBlock:
     let pragmaList = n.sons[0]
     let oldLocked = tracked.locked.len
@@ -844,31 +844,31 @@ proc track(tracked: PEffects, n: PNode) =
   else:
     for i in 0 ..< safeLen(n): track(tracked, n.sons[i])
 
-proc subtypeRelation(spec, real: PNode): bool =
-  result = safeInheritanceDiff(real.excType, spec.typ) <= 0
+proc subtypeRelation(g: ModuleGraph; spec, real: PNode): bool =
+  result = safeInheritanceDiff(g.excType(real), spec.typ) <= 0
 
-proc checkRaisesSpec(spec, real: PNode, msg: string, hints: bool;
-                     effectPredicate: proc (a, b: PNode): bool {.nimcall.}) =
+proc checkRaisesSpec(g: ModuleGraph; spec, real: PNode, msg: string, hints: bool;
+                     effectPredicate: proc (g: ModuleGraph; a, b: PNode): bool {.nimcall.}) =
   # check that any real exception is listed in 'spec'; mark those as used;
   # report any unused exception
   var used = initIntSet()
   for r in items(real):
     block search:
       for s in 0 ..< spec.len:
-        if effectPredicate(spec[s], r):
+        if effectPredicate(g, spec[s], r):
           used.incl(s)
           break search
       # XXX call graph analysis would be nice here!
       pushInfoContext(spec.info)
-      localError(r.info, errGenerated, msg & typeToString(r.typ))
+      localError(g.config, r.info, errGenerated, msg & typeToString(r.typ))
       popInfoContext()
   # hint about unnecessarily listed exception types:
   if hints:
     for s in 0 ..< spec.len:
       if not used.contains(s):
-        message(spec[s].info, hintXDeclaredButNotUsed, renderTree(spec[s]))
+        message(g.config, spec[s].info, hintXDeclaredButNotUsed, renderTree(spec[s]))
 
-proc checkMethodEffects*(disp, branch: PSym) =
+proc checkMethodEffects*(g: ModuleGraph; disp, branch: PSym) =
   ## checks for consistent effects for multi methods.
   let actual = branch.typ.n.sons[0]
   if actual.len != effectListLen: return
@@ -876,42 +876,42 @@ proc checkMethodEffects*(disp, branch: PSym) =
   let p = disp.ast.sons[pragmasPos]
   let raisesSpec = effectSpec(p, wRaises)
   if not isNil(raisesSpec):
-    checkRaisesSpec(raisesSpec, actual.sons[exceptionEffects],
+    checkRaisesSpec(g, raisesSpec, actual.sons[exceptionEffects],
       "can raise an unlisted exception: ", hints=off, subtypeRelation)
   let tagsSpec = effectSpec(p, wTags)
   if not isNil(tagsSpec):
-    checkRaisesSpec(tagsSpec, actual.sons[tagEffects],
+    checkRaisesSpec(g, tagsSpec, actual.sons[tagEffects],
       "can have an unlisted effect: ", hints=off, subtypeRelation)
   if sfThread in disp.flags and notGcSafe(branch.typ):
-    localError(branch.info, "base method is GC-safe, but '$1' is not" %
+    localError(g.config, branch.info, "base method is GC-safe, but '$1' is not" %
                                 branch.name.s)
   if branch.typ.lockLevel > disp.typ.lockLevel:
     when true:
-      message(branch.info, warnLockLevel,
+      message(g.config, branch.info, warnLockLevel,
         "base method has lock level $1, but dispatcher has $2" %
           [$branch.typ.lockLevel, $disp.typ.lockLevel])
     else:
       # XXX make this an error after bigbreak has been released:
-      localError(branch.info,
+      localError(g.config, branch.info,
         "base method has lock level $1, but dispatcher has $2" %
           [$branch.typ.lockLevel, $disp.typ.lockLevel])
 
-proc setEffectsForProcType*(t: PType, n: PNode) =
+proc setEffectsForProcType*(g: ModuleGraph; t: PType, n: PNode) =
   var effects = t.n.sons[0]
-  internalAssert t.kind == tyProc and effects.kind == nkEffectList
+  if t.kind != tyProc or effects.kind != nkEffectList: return
 
   let
     raisesSpec = effectSpec(n, wRaises)
     tagsSpec = effectSpec(n, wTags)
   if not isNil(raisesSpec) or not isNil(tagsSpec):
-    internalAssert effects.len == 0
+    internalAssert g.config, effects.len == 0
     newSeq(effects.sons, effectListLen)
     if not isNil(raisesSpec):
       effects.sons[exceptionEffects] = raisesSpec
     if not isNil(tagsSpec):
       effects.sons[tagEffects] = tagsSpec
 
-proc initEffects(effects: PNode; s: PSym; t: var TEffects) =
+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)
@@ -922,52 +922,55 @@ proc initEffects(effects: PNode; s: PSym; t: var TEffects) =
   t.tags = effects.sons[tagEffects]
   t.owner = s
   t.init = @[]
-  t.guards = @[]
+  t.guards.s = @[]
+  t.guards.o = initOperators(g)
   t.locked = @[]
+  t.graph = g
+  t.config = g.config
 
-proc trackProc*(s: PSym, body: PNode) =
+proc trackProc*(g: ModuleGraph; s: PSym, body: PNode) =
   var effects = s.typ.n.sons[0]
-  internalAssert effects.kind == nkEffectList
+  if effects.kind != nkEffectList: return
   # effects already computed?
   if sfForward in s.flags: return
   if effects.len == effectListLen: return
 
   var t: TEffects
-  initEffects(effects, s, t)
+  initEffects(g, effects, s, t)
   track(t, body)
   if not isEmptyType(s.typ.sons[0]) and
       {tfNeedsInit, tfNotNil} * s.typ.sons[0].flags != {} and
       s.kind in {skProc, skFunc, skConverter, skMethod}:
     var res = s.ast.sons[resultPos].sym # get result symbol
     if res.id notin t.init:
-      message(body.info, warnProveInit, "result")
+      message(g.config, body.info, warnProveInit, "result")
   let p = s.ast.sons[pragmasPos]
   let raisesSpec = effectSpec(p, wRaises)
   if not isNil(raisesSpec):
-    checkRaisesSpec(raisesSpec, t.exc, "can raise an unlisted exception: ",
+    checkRaisesSpec(g, raisesSpec, t.exc, "can raise an unlisted exception: ",
                     hints=on, subtypeRelation)
     # after the check, use the formal spec:
     effects.sons[exceptionEffects] = raisesSpec
 
   let tagsSpec = effectSpec(p, wTags)
   if not isNil(tagsSpec):
-    checkRaisesSpec(tagsSpec, t.tags, "can have an unlisted effect: ",
+    checkRaisesSpec(g, tagsSpec, t.tags, "can have an unlisted effect: ",
                     hints=off, subtypeRelation)
     # after the check, use the formal spec:
     effects.sons[tagEffects] = tagsSpec
 
   if sfThread in s.flags and t.gcUnsafe:
-    if optThreads in gGlobalOptions and optThreadAnalysis in gGlobalOptions:
+    if optThreads in g.config.globalOptions and optThreadAnalysis in g.config.globalOptions:
       #localError(s.info, "'$1' is not GC-safe" % s.name.s)
-      listGcUnsafety(s, onlyWarning=false)
+      listGcUnsafety(s, onlyWarning=false, g.config)
     else:
-      listGcUnsafety(s, onlyWarning=true)
+      listGcUnsafety(s, onlyWarning=true, g.config)
       #localError(s.info, warnGcUnsafe2, s.name.s)
   if sfNoSideEffect in s.flags and t.hasSideEffect:
     when false:
-      listGcUnsafety(s, onlyWarning=false)
+      listGcUnsafety(s, onlyWarning=false, g.config)
     else:
-      localError(s.info, errXhasSideEffects, s.name.s)
+      localError(g.config, s.info, "'$1' can have side effects" % s.name.s)
   if not t.gcUnsafe:
     s.typ.flags.incl tfGcSafe
   if not t.hasSideEffect and sfSideEffect notin s.flags:
@@ -976,7 +979,7 @@ proc trackProc*(s: PSym, body: PNode) =
     s.typ.lockLevel = t.maxLockLevel
   elif t.maxLockLevel > s.typ.lockLevel:
     #localError(s.info,
-    message(s.info, warnLockLevel,
+    message(g.config, s.info, warnLockLevel,
       "declared lock level is $1, but real lock level is $2" %
         [$s.typ.lockLevel, $t.maxLockLevel])
   when defined(useDfa):
@@ -984,12 +987,12 @@ proc trackProc*(s: PSym, body: PNode) =
       dataflowAnalysis(s, body)
       when false: trackWrites(s, body)
 
-proc trackTopLevelStmt*(module: PSym; n: PNode) =
+proc trackTopLevelStmt*(g: ModuleGraph; module: PSym; n: PNode) =
   if n.kind in {nkPragma, nkMacroDef, nkTemplateDef, nkProcDef, nkFuncDef,
                 nkTypeSection, nkConverterDef, nkMethodDef, nkIteratorDef}:
     return
   var effects = newNode(nkEffectList, n.info)
   var t: TEffects
-  initEffects(effects, module, t)
+  initEffects(g, effects, module, t)
   t.isToplevel = true
   track(t, n)
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index f90e5c5f9..3687e50e9 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -10,51 +10,77 @@
 ## this module does the semantic checking of statements
 #  included from sem.nim
 
-var enforceVoidContext = PType(kind: tyStmt)
+const
+  errNoSymbolToBorrowFromFound = "no symbol to borrow from found"
+  errDiscardValueX = "value of type '$1' has to be discarded"
+  errInvalidDiscard = "statement returns no value that can be discarded"
+  errInvalidControlFlowX = "invalid control flow: $1"
+  errSelectorMustBeOfCertainTypes = "selector must be of an ordinal type, float or string"
+  errExprCannotBeRaised = "only a 'ref object' can be raised"
+  errBreakOnlyInLoop = "'break' only allowed in loop construct"
+  errExceptionAlreadyHandled = "exception already handled"
+  errYieldNotAllowedHere = "'yield' only allowed in an iterator"
+  errYieldNotAllowedInTryStmt = "'yield' cannot be used within 'try' in a non-inlined iterator"
+  errInvalidNumberOfYieldExpr = "invalid number of 'yield' expressions"
+  errCannotReturnExpr = "current routine cannot return an expression"
+  errGenericLambdaNotAllowed = "A nested proc can have generic parameters only when " &
+    "it is used as an operand to another routine and the types " &
+    "of the generic paramers can be inferred from the expected signature."
+  errCannotInferTypeOfTheLiteral = "cannot infer the type of the $1"
+  errCannotInferReturnType = "cannot infer the return type of the proc"
+  errCannotInferStaticParam = "cannot infer the value of the static param '$1'"
+  errProcHasNoConcreteType = "'$1' doesn't have a concrete type, due to unspecified generic parameters."
+  errLetNeedsInit = "'let' symbol requires an initialization"
+  errThreadvarCannotInit = "a thread var cannot be initialized explicitly; this would only run for the main thread"
+  errImplOfXexpected = "implementation of '$1' expected"
+  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)
+  checkSonsLen(n, 1, c.config)
   if n.sons[0].kind != nkEmpty:
     n.sons[0] = semExprWithType(c, n.sons[0])
     if isEmptyType(n.sons[0].typ) or n.sons[0].typ.kind == tyNone or n.sons[0].kind == nkTypeOfExpr:
-      localError(n.info, errInvalidDiscard)
+      localError(c.config, n.info, errInvalidDiscard)
 
 proc semBreakOrContinue(c: PContext, n: PNode): PNode =
   result = n
-  checkSonsLen(n, 1)
+  checkSonsLen(n, 1, c.config)
   if n.sons[0].kind != nkEmpty:
     if n.kind != nkContinueStmt:
       var s: PSym
       case n.sons[0].kind
       of nkIdent: s = lookUp(c, n.sons[0])
       of nkSym: s = n.sons[0].sym
-      else: illFormedAst(n)
+      else: illFormedAst(n, c.config)
       s = getGenSym(c, s)
       if s.kind == skLabel and s.owner.id == c.p.owner.id:
         var x = newSymNode(s)
         x.info = n.info
         incl(s.flags, sfUsed)
         n.sons[0] = x
-        suggestSym(x.info, s, c.graph.usageSym)
+        suggestSym(c.config, x.info, s, c.graph.usageSym)
         styleCheckUse(x.info, s)
       else:
-        localError(n.info, errInvalidControlFlowX, s.name.s)
+        localError(c.config, n.info, errInvalidControlFlowX % s.name.s)
     else:
-      localError(n.info, errGenerated, "'continue' cannot have a label")
+      localError(c.config, n.info, errGenerated, "'continue' cannot have a label")
   elif (c.p.nestedLoopCounter <= 0) and (c.p.nestedBlockCounter <= 0):
-    localError(n.info, errInvalidControlFlowX,
+    localError(c.config, n.info, errInvalidControlFlowX %
                renderTree(n, {renderNoComments}))
 
-proc semAsm(con: PContext, n: PNode): PNode =
-  checkSonsLen(n, 2)
-  var marker = pragmaAsm(con, n.sons[0])
+proc semAsm(c: PContext, n: PNode): PNode =
+  checkSonsLen(n, 2, c.config)
+  var marker = pragmaAsm(c, n.sons[0])
   if marker == '\0': marker = '`' # default marker
-  result = semAsmOrEmit(con, n, marker)
+  result = semAsmOrEmit(c, n, marker)
 
 proc semWhile(c: PContext, n: PNode): PNode =
   result = n
-  checkSonsLen(n, 2)
+  checkSonsLen(n, 2, c.config)
   openScope(c)
   n.sons[0] = forceBool(c, semExprWithType(c, n.sons[0]))
   inc(c.p.nestedLoopCounter)
@@ -71,37 +97,12 @@ proc toCover(t: PType): BiggestInt =
   else:
     result = lengthOrd(skipTypes(t, abstractVar-{tyTypeDesc}))
 
-when false:
-  proc performProcvarCheck(c: PContext, info: TLineInfo, s: PSym) =
-    ## Checks that the given symbol is a proper procedure variable, meaning
-    ## that it
-    var smoduleId = getModule(s).id
-    if sfProcvar notin s.flags and s.typ.callConv == ccDefault and
-        smoduleId != c.module.id:
-      block outer:
-        for module in c.friendModules:
-          if smoduleId == module.id:
-            break outer
-        localError(info, errXCannotBePassedToProcVar, s.name.s)
-
-template semProcvarCheck(c: PContext, n: PNode) =
-  when false:
-    var n = n.skipConv
-    if n.kind in nkSymChoices:
-      for x in n:
-        if x.sym.kind in {skProc, skMethod, skConverter, skIterator}:
-          performProcvarCheck(c, n.info, x.sym)
-    elif n.kind == nkSym and n.sym.kind in {skProc, skMethod, skConverter,
-                                          skIterator}:
-      performProcvarCheck(c, n.info, n.sym)
-
 proc semProc(c: PContext, n: PNode): PNode
 
 proc semExprBranch(c: PContext, n: PNode): PNode =
   result = semExpr(c, n)
   if result.typ != nil:
     # XXX tyGenericInst here?
-    semProcvarCheck(c, result)
     if result.typ.kind in {tyVar, tyLent}: result = newDeref(result)
 
 proc semExprBranchScope(c: PContext, n: PNode): PNode =
@@ -120,42 +121,35 @@ proc implicitlyDiscardable(n: PNode): bool =
   result = isCallExpr(n) and n.sons[0].kind == nkSym and
            sfDiscardable in n.sons[0].sym.flags
 
-proc fixNilType(n: PNode) =
+proc fixNilType(c: PContext; n: PNode) =
   if isAtom(n):
     if n.kind != nkNilLit and n.typ != nil:
-      localError(n.info, errDiscardValueX, n.typ.typeToString)
+      localError(c.config, n.info, errDiscardValueX % n.typ.typeToString)
   elif n.kind in {nkStmtList, nkStmtListExpr}:
     n.kind = nkStmtList
-    for it in n: fixNilType(it)
+    for it in n: fixNilType(c, it)
   n.typ = nil
 
 proc discardCheck(c: PContext, result: PNode) =
   if c.matchedConcept != nil: return
   if result.typ != nil and result.typ.kind notin {tyStmt, tyVoid}:
-    if result.kind == nkNilLit:
-      result.typ = nil
-      message(result.info, warnNilStatement)
-    elif implicitlyDiscardable(result):
+    if implicitlyDiscardable(result):
       var n = result
       result.typ = nil
       while n.kind in skipForDiscardable:
         n = n.lastSon
         n.typ = nil
-    elif result.typ.kind != tyError and gCmd != cmdInteractive:
-      if result.typ.kind == tyNil:
-        fixNilType(result)
-        message(result.info, warnNilStatement)
-      else:
-        var n = result
-        while n.kind in skipForDiscardable: n = n.lastSon
-        var s = "expression '" & $n & "' is of type '" &
-            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
-        if result.typ.kind == tyProc:
-          s.add "; for a function call use ()"
-        localError(n.info, s)
+    elif result.typ.kind != tyError and c.config.cmd != cmdInteractive:
+      var n = result
+      while n.kind in skipForDiscardable: n = n.lastSon
+      var s = "expression '" & $n & "' is of type '" &
+          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
+      if result.typ.kind == tyProc:
+        s.add "; for a function call use ()"
+      localError(c.config, n.info, s)
 
 proc semIf(c: PContext, n: PNode): PNode =
   result = n
@@ -174,7 +168,7 @@ proc semIf(c: PContext, n: PNode): PNode =
       hasElse = true
       it.sons[0] = semExprBranchScope(c, it.sons[0])
       typ = commonType(typ, it.sons[0])
-    else: illFormedAst(it)
+    else: illFormedAst(it, c.config)
   if isEmptyType(typ) or typ.kind in {tyNil, tyExpr} or not hasElse:
     for it in n: discardCheck(c, it.lastSon)
     result.kind = nkIfStmt
@@ -190,7 +184,7 @@ proc semIf(c: PContext, n: PNode): PNode =
 
 proc semCase(c: PContext, n: PNode): PNode =
   result = n
-  checkMinSonsLen(n, 2)
+  checkMinSonsLen(n, 2, c.config)
   openScope(c)
   n.sons[0] = semExprWithType(c, n.sons[0])
   var chckCovered = false
@@ -204,23 +198,23 @@ proc semCase(c: PContext, n: PNode): PNode =
   of tyFloat..tyFloat128, tyString, tyError:
     discard
   else:
-    localError(n.info, errSelectorMustBeOfCertainTypes)
+    localError(c.config, n.info, errSelectorMustBeOfCertainTypes)
     return
   for i in countup(1, sonsLen(n) - 1):
     var x = n.sons[i]
     when defined(nimsuggest):
-      if gIdeCmd == ideSug and exactEquals(gTrackPos, x.info) and caseTyp.kind == tyEnum:
+      if c.config.ideCmd == ideSug and exactEquals(gTrackPos, x.info) and caseTyp.kind == tyEnum:
         suggestEnum(c, x, caseTyp)
     case x.kind
     of nkOfBranch:
-      checkMinSonsLen(x, 2)
+      checkMinSonsLen(x, 2, c.config)
       semCaseBranch(c, n, x, i, covered)
       var last = sonsLen(x)-1
       x.sons[last] = semExprBranchScope(c, x.sons[last])
       typ = commonType(typ, x.sons[last])
     of nkElifBranch:
       chckCovered = false
-      checkSonsLen(x, 2)
+      checkSonsLen(x, 2, c.config)
       when newScopeForIf: openScope(c)
       x.sons[0] = forceBool(c, semExprWithType(c, x.sons[0]))
       when not newScopeForIf: openScope(c)
@@ -229,17 +223,17 @@ proc semCase(c: PContext, n: PNode): PNode =
       closeScope(c)
     of nkElse:
       chckCovered = false
-      checkSonsLen(x, 1)
+      checkSonsLen(x, 1, c.config)
       x.sons[0] = semExprBranchScope(c, x.sons[0])
       typ = commonType(typ, x.sons[0])
       hasElse = true
     else:
-      illFormedAst(x)
+      illFormedAst(x, c.config)
   if chckCovered:
     if covered == toCover(n.sons[0].typ):
       hasElse = true
     else:
-      localError(n.info, errNotAllCasesCovered)
+      localError(c.config, n.info, "not all cases are covered")
   closeScope(c)
   if isEmptyType(typ) or typ.kind in {tyNil, tyExpr} or not hasElse:
     for i in 1..n.len-1: discardCheck(c, n.sons[i].lastSon)
@@ -261,19 +255,19 @@ proc semTry(c: PContext, n: PNode): PNode =
     # returns true if exception type is imported type
     let typ = semTypeNode(c, typeNode, nil).toObject()
     var is_imported = false
-    if isImportedException(typ):
+    if isImportedException(typ, c.config):
       is_imported = true
     elif not isException(typ):
-      localError(typeNode.info, errExprCannotBeRaised)
+      localError(c.config, typeNode.info, errExprCannotBeRaised)
 
     if containsOrIncl(check, typ.id):
-      localError(typeNode.info, errExceptionAlreadyHandled)
+      localError(c.config, typeNode.info, errExceptionAlreadyHandled)
     typeNode = newNodeIT(nkType, typeNode.info, typ)
     is_imported
 
   result = n
   inc c.p.inTryStmt
-  checkMinSonsLen(n, 2)
+  checkMinSonsLen(n, 2, c.config)
 
   var typ = commonTypeBegin
   n[0] = semExprBranchScope(c, n[0])
@@ -282,7 +276,7 @@ proc semTry(c: PContext, n: PNode): PNode =
   var last = sonsLen(n) - 1
   for i in countup(1, last):
     let a = n.sons[i]
-    checkMinSonsLen(a, 1)
+    checkMinSonsLen(a, 1, c.config)
     openScope(c)
     if a.kind == nkExceptBranch:
 
@@ -309,10 +303,10 @@ proc semTry(c: PContext, n: PNode): PNode =
           else: is_native = true
 
         if is_native and is_imported:
-          localError(a[0].info, "Mix of imported and native exception types is not allowed in one except branch")
+          localError(c.config, a[0].info, "Mix of imported and native exception types is not allowed in one except branch")
 
     elif a.kind != nkFinally:
-      illFormedAst(n)
+      illFormedAst(n, c.config)
 
     # last child of an nkExcept/nkFinally branch is a statement:
     a[^1] = semExprBranchScope(c, a[^1])
@@ -344,10 +338,10 @@ proc fitRemoveHiddenConv(c: PContext, typ: PType, n: PNode): PNode =
       result.info = n.info
       result.typ = typ
     else:
-      changeType(r1, typ, check=true)
+      changeType(c, r1, typ, check=true)
       result = r1
   elif not sameType(result.typ, typ):
-    changeType(result, typ, check=false)
+    changeType(c, result, typ, check=false)
 
 proc findShadowedVar(c: PContext, v: PSym): PSym =
   for scope in walkScopes(c.currentScope.parent):
@@ -371,44 +365,23 @@ proc semIdentDef(c: PContext, n: PNode, kind: TSymKind): PSym =
     result = semIdentWithPragma(c, kind, n, {})
     if result.owner.kind == skModule:
       incl(result.flags, sfGlobal)
-  suggestSym(n.info, result, c.graph.usageSym)
+  suggestSym(c.config, n.info, result, c.graph.usageSym)
   styleCheckDef(result)
 
-proc checkNilable(v: PSym) =
+proc checkNilable(c: PContext; v: PSym) =
   if {sfGlobal, sfImportC} * v.flags == {sfGlobal} and
       {tfNotNil, tfNeedsInit} * v.typ.flags != {}:
     if v.ast.isNil:
-      message(v.info, warnProveInit, v.name.s)
+      message(c.config, v.info, warnProveInit, v.name.s)
     elif tfNotNil in v.typ.flags and tfNotNil notin v.ast.typ.flags:
-      message(v.info, warnProveInit, v.name.s)
+      message(c.config, v.info, warnProveInit, v.name.s)
 
 include semasgn
 
-proc addToVarSection(c: PContext; result: var PNode; orig, identDefs: PNode) =
-  # consider this:
-  #   var
-  #     x = 0
-  #     withOverloadedAssignment = foo()
-  #     y = use(withOverloadedAssignment)
-  # We need to split this into a statement list with multiple 'var' sections
-  # in order for this transformation to be correct.
+proc addToVarSection(c: PContext; result: PNode; orig, identDefs: PNode) =
   let L = identDefs.len
   let value = identDefs[L-1]
-  if value.typ != nil and tfHasAsgn in value.typ.flags and not newDestructors:
-    # the spec says we need to rewrite 'var x = T()' to 'var x: T; x = T()':
-    identDefs.sons[L-1] = emptyNode
-    if result.kind != nkStmtList:
-      let oldResult = result
-      oldResult.add identDefs
-      result = newNodeI(nkStmtList, result.info)
-      result.add oldResult
-    else:
-      let o = copyNode(orig)
-      o.add identDefs
-      result.add o
-    for i in 0 .. L-3:
-      result.add overloadedAsgn(c, identDefs[i], value)
-  elif result.kind == nkStmtList:
+  if result.kind == nkStmtList:
     let o = copyNode(orig)
     o.add identDefs
     result.add o
@@ -422,13 +395,13 @@ proc isDiscardUnderscore(v: PSym): bool =
 
 proc semUsing(c: PContext; n: PNode): PNode =
   result = ast.emptyNode
-  if not isTopLevel(c): localError(n.info, errXOnlyAtModuleScope, "using")
+  if not isTopLevel(c): localError(c.config, n.info, errXOnlyAtModuleScope % "using")
   for i in countup(0, sonsLen(n)-1):
     var a = n.sons[i]
-    if gCmd == cmdIdeTools: suggestStmt(c, a)
+    if c.config.cmd == cmdIdeTools: suggestStmt(c, a)
     if a.kind == nkCommentStmt: continue
-    if a.kind notin {nkIdentDefs, nkVarTuple, nkConstDef}: illFormedAst(a)
-    checkMinSonsLen(a, 3)
+    if a.kind notin {nkIdentDefs, nkVarTuple, nkConstDef}: illFormedAst(a, c.config)
+    checkMinSonsLen(a, 3, c.config)
     var length = sonsLen(a)
     if a.sons[length-2].kind != nkEmpty:
       let typ = semTypeNode(c, a.sons[length-2], nil)
@@ -437,10 +410,10 @@ proc semUsing(c: PContext; n: PNode): PNode =
         v.typ = typ
         strTableIncl(c.signatures, v)
     else:
-      localError(a.info, "'using' section must have a type")
+      localError(c.config, a.info, "'using' section must have a type")
     var def: PNode
     if a.sons[length-1].kind != nkEmpty:
-      localError(a.info, "'using' sections cannot contain assignments")
+      localError(c.config, a.info, "'using' sections cannot contain assignments")
 
 proc hasEmpty(typ: PType): bool =
   if typ.kind in {tySequence, tyArray, tySet}:
@@ -469,7 +442,7 @@ 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(n[1])
+    let y = considerQuotedIdent(c.config, 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)
@@ -480,14 +453,14 @@ proc fillPartialObject(c: PContext; n: PNode; typ: PType) =
       n.sons[1] = newSymNode(field)
       n.typ = field.typ
     else:
-      localError(n.info, "implicit object field construction " &
+      localError(c.config, n.info, "implicit object field construction " &
         "requires a .partial object, but got " & typeToString(obj))
   else:
-    localError(n.info, "nkDotNode requires 2 children")
+    localError(c.config, n.info, "nkDotNode requires 2 children")
 
-proc setVarType(v: PSym, typ: PType) =
+proc setVarType(c: PContext; v: PSym, typ: PType) =
   if v.typ != nil and not sameTypeOrNil(v.typ, typ):
-    localError(v.info, "inconsistent typing for reintroduced symbol '" &
+    localError(c.config, v.info, "inconsistent typing for reintroduced symbol '" &
         v.name.s & "': previous type was: " & typeToString(v.typ) &
         "; new type is: " & typeToString(typ))
   v.typ = typ
@@ -498,10 +471,10 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
   var hasCompileTime = false
   for i in countup(0, sonsLen(n)-1):
     var a = n.sons[i]
-    if gCmd == cmdIdeTools: suggestStmt(c, a)
+    if c.config.cmd == cmdIdeTools: suggestStmt(c, a)
     if a.kind == nkCommentStmt: continue
-    if a.kind notin {nkIdentDefs, nkVarTuple, nkConstDef}: illFormedAst(a)
-    checkMinSonsLen(a, 3)
+    if a.kind notin {nkIdentDefs, nkVarTuple, nkConstDef}: illFormedAst(a, c.config)
+    checkMinSonsLen(a, 3, c.config)
     var length = sonsLen(a)
     var typ: PType
     if a.sons[length-2].kind != nkEmpty:
@@ -513,7 +486,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
       def = semExprWithType(c, a.sons[length-1], {efAllowDestructor})
       if def.typ.kind == tyTypeDesc and c.p.owner.kind != skMacro:
         # prevent the all too common 'var x = int' bug:
-        localError(def.info, "'typedesc' metatype is not valid here; typed '=' instead of ':'?")
+        localError(c.config, def.info, "'typedesc' metatype is not valid here; typed '=' instead of ':'?")
         def.typ = errorType(c)
       if typ != nil:
         if typ.isMetaType:
@@ -529,32 +502,31 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
         if typ.kind in tyUserTypeClasses and typ.isResolvedUserTypeClass:
           typ = typ.lastSon
         if hasEmpty(typ):
-          localError(def.info, errCannotInferTypeOfTheLiteral,
+          localError(c.config, def.info, errCannotInferTypeOfTheLiteral %
                      ($typ.kind).substr(2).toLowerAscii)
         elif typ.kind == tyProc and tfUnresolved in typ.flags:
-          localError(def.info, errProcHasNoConcreteType, def.renderTree)
+          localError(c.config, def.info, errProcHasNoConcreteType % def.renderTree)
     else:
-      if symkind == skLet: localError(a.info, errLetNeedsInit)
+      if symkind == skLet: localError(c.config, a.info, errLetNeedsInit)
 
     # this can only happen for errornous var statements:
     if typ == nil: continue
-    typeAllowedCheck(a.info, typ, symkind, if c.matchedConcept != nil: {taConcept} else: {})
+    typeAllowedCheck(c.config, a.info, typ, symkind, if c.matchedConcept != nil: {taConcept} else: {})
     liftTypeBoundOps(c, typ, a.info)
     var tup = skipTypes(typ, {tyGenericInst, tyAlias, tySink})
     if a.kind == nkVarTuple:
       if tup.kind != tyTuple:
-        localError(a.info, errXExpected, "tuple")
+        localError(c.config, a.info, errXExpected, "tuple")
       elif length-2 != sonsLen(tup):
-        localError(a.info, errWrongNumberOfVariables)
-      else:
-        b = newNodeI(nkVarTuple, a.info)
-        newSons(b, length)
-        b.sons[length-2] = a.sons[length-2] # keep type desc for doc generator
-        b.sons[length-1] = def
-        addToVarSection(c, result, n, b)
+        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
+      b.sons[length-1] = def
+      addToVarSection(c, result, n, b)
     elif tup.kind == tyTuple and def.kind in {nkPar, nkTupleConstr} and
         a.kind == nkIdentDefs and a.len > 3:
-      message(a.info, warnEachIdentIsTuple)
+      message(c.config, a.info, warnEachIdentIsTuple)
 
     for j in countup(0, length-3):
       if a[j].kind == nkDotExpr:
@@ -572,19 +544,19 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
           if shadowed != nil:
             shadowed.flags.incl(sfShadowed)
             if shadowed.kind == skResult and sfGenSym notin v.flags:
-              message(a.info, warnResultShadowed)
+              message(c.config, a.info, warnResultShadowed)
             # a shadowed variable is an error unless it appears on the right
             # side of the '=':
-            if warnShadowIdent in gNotes and not identWithin(def, v.name):
-              message(a.info, warnShadowIdent, v.name.s)
+            if warnShadowIdent in c.config.notes and not identWithin(def, v.name):
+              message(c.config, a.info, warnShadowIdent, v.name.s)
       if a.kind != nkVarTuple:
         if def.kind != nkEmpty:
           # this is needed for the evaluation pass and for the guard checking:
           v.ast = def
-          if sfThread in v.flags: localError(def.info, errThreadvarCannotInit)
-        setVarType(v, typ)
+          if sfThread in v.flags: localError(c.config, def.info, errThreadvarCannotInit)
+        setVarType(c, v, typ)
         b = newNodeI(nkIdentDefs, a.info)
-        if importantComments():
+        if importantComments(c.config):
           # keep documentation information:
           b.comment = a.comment
         addSon(b, newSymNode(v))
@@ -593,27 +565,30 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
         addToVarSection(c, result, n, b)
       else:
         if def.kind in {nkPar, nkTupleConstr}: v.ast = def[j]
-        setVarType(v, tup.sons[j])
+        # bug #7663, for 'nim check' this can be a non-tuple:
+        if tup.kind == tyTuple: setVarType(c, v, tup.sons[j])
+        else: v.typ = tup
         b.sons[j] = newSymNode(v)
-      checkNilable(v)
+      checkNilable(c, v)
       if sfCompileTime in v.flags: hasCompileTime = true
-  if hasCompileTime: vm.setupCompileTimeVar(c.module, c.cache, result)
+  if hasCompileTime:
+    vm.setupCompileTimeVar(c.module, c.cache, c.graph, result)
 
 proc semConst(c: PContext, n: PNode): PNode =
   result = copyNode(n)
   for i in countup(0, sonsLen(n) - 1):
     var a = n.sons[i]
-    if gCmd == cmdIdeTools: suggestStmt(c, a)
+    if c.config.cmd == cmdIdeTools: suggestStmt(c, a)
     if a.kind == nkCommentStmt: continue
-    if (a.kind != nkConstDef): illFormedAst(a)
-    checkSonsLen(a, 3)
+    if a.kind != nkConstDef: illFormedAst(a, c.config)
+    checkSonsLen(a, 3, c.config)
     var v = semIdentDef(c, a.sons[0], skConst)
     var typ: PType = nil
     if a.sons[1].kind != nkEmpty: typ = semTypeNode(c, a.sons[1], nil)
 
     var def = semConstExpr(c, a.sons[2])
     if def == nil:
-      localError(a.sons[2].info, errConstExprExpected)
+      localError(c.config, a.sons[2].info, errConstExprExpected)
       continue
     # check type compatibility between def.typ and typ:
     if typ != nil:
@@ -621,16 +596,16 @@ proc semConst(c: PContext, n: PNode): PNode =
     else:
       typ = def.typ
     if typ == nil:
-      localError(a.sons[2].info, errConstExprExpected)
+      localError(c.config, a.sons[2].info, errConstExprExpected)
       continue
     if typeAllowed(typ, skConst) != nil and def.kind != nkNilLit:
-      localError(a.info, "invalid type for const: " & typeToString(typ))
+      localError(c.config, a.info, "invalid type for const: " & typeToString(typ))
       continue
-    setVarType(v, typ)
+    setVarType(c, v, typ)
     v.ast = def               # no need to copy
     if sfGenSym notin v.flags: addInterfaceDecl(c, v)
     var b = newNodeI(nkConstDef, a.info)
-    if importantComments(): b.comment = a.comment
+    if importantComments(c.config): b.comment = a.comment
     addSon(b, newSymNode(v))
     addSon(b, a.sons[1])
     addSon(b, copyTree(def))
@@ -639,12 +614,12 @@ proc semConst(c: PContext, n: PNode): PNode =
 include semfields
 
 proc addForVarDecl(c: PContext, v: PSym) =
-  if warnShadowIdent in gNotes:
+  if warnShadowIdent in c.config.notes:
     let shadowed = findShadowedVar(c, v)
     if shadowed != nil:
       # XXX should we do this here?
       #shadowed.flags.incl(sfShadowed)
-      message(v.info, warnShadowIdent, v.name.s)
+      message(c.config, v.info, warnShadowIdent, v.name.s)
   addDecl(c, v)
 
 proc symForVar(c: PContext, n: PNode): PSym =
@@ -670,9 +645,9 @@ proc semForVars(c: PContext, n: PNode): PNode =
       n.sons[0] = newSymNode(v)
       if sfGenSym notin v.flags: addForVarDecl(c, v)
     else:
-      localError(n.info, errWrongNumberOfVariables)
+      localError(c.config, n.info, errWrongNumberOfVariables)
   elif length-2 != sonsLen(iter):
-    localError(n.info, errWrongNumberOfVariables)
+    localError(c.config, n.info, errWrongNumberOfVariables)
   else:
     for i in countup(0, length - 3):
       var v = symForVar(c, n.sons[i])
@@ -709,7 +684,7 @@ proc handleForLoopMacro(c: PContext; n: PNode): PNode =
     # n := for a, b, c in m(x, y, z): Y
     # to
     # m(n)
-    let forLoopStmt = magicsys.getCompilerProc("ForLoopStmt")
+    let forLoopStmt = magicsys.getCompilerProc(c.graph, "ForLoopStmt")
     if forLoopStmt == nil: return
 
     let headSymbol = iterExpr[0]
@@ -722,7 +697,7 @@ proc handleForLoopMacro(c: PContext; n: PNode): PNode =
           if match == nil:
             match = symx
           else:
-            localError(n.info, errGenerated, msgKindToString(errAmbiguousCallXYZ) % [
+            localError(c.config, n.info, errAmbiguousCallXYZ % [
               getProcHeader(match), getProcHeader(symx), $iterExpr])
       symx = nextOverloadIter(o, c, headSymbol)
 
@@ -736,7 +711,7 @@ proc handleForLoopMacro(c: PContext; n: PNode): PNode =
     else: result = nil
 
 proc semFor(c: PContext, n: PNode): PNode =
-  checkMinSonsLen(n, 3)
+  checkMinSonsLen(n, 3, c.config)
   var length = sonsLen(n)
   result = handleForLoopMacro(c, n)
   if result != nil: return result
@@ -766,7 +741,7 @@ proc semFor(c: PContext, n: PNode): PNode =
     elif length == 4:
       n.sons[length-2] = implicitIterator(c, "pairs", n.sons[length-2])
     else:
-      localError(n.sons[length-2].info, errIteratorExpected)
+      localError(c.config, n.sons[length-2].info, "iterator within for loop context expected")
     result = semForVars(c, n)
   else:
     result = semForVars(c, n)
@@ -777,36 +752,31 @@ proc semFor(c: PContext, n: PNode): PNode =
 
 proc semRaise(c: PContext, n: PNode): PNode =
   result = n
-  checkSonsLen(n, 1)
+  checkSonsLen(n, 1, c.config)
   if n[0].kind != nkEmpty:
-
     n[0] = semExprWithType(c, n[0])
     let typ = n[0].typ
-
-    if not isImportedException(typ):
-
+    if not isImportedException(typ, c.config):
       if typ.kind != tyRef or typ.lastSon.kind != tyObject:
-        localError(n.info, errExprCannotBeRaised)
-
+        localError(c.config, n.info, errExprCannotBeRaised)
       if not isException(typ.lastSon):
-        localError(n.info, "raised object of type $1 does not inherit from Exception",
+        localError(c.config, n.info, "raised object of type $1 does not inherit from Exception",
                           [typeToString(typ)])
 
-
 proc addGenericParamListToScope(c: PContext, n: PNode) =
-  if n.kind != nkGenericParams: illFormedAst(n)
+  if n.kind != nkGenericParams: illFormedAst(n, c.config)
   for i in countup(0, sonsLen(n)-1):
     var a = n.sons[i]
     if a.kind == nkSym: addDecl(c, a.sym)
-    else: illFormedAst(a)
+    else: illFormedAst(a, c.config)
 
-proc typeSectionTypeName(n: PNode): PNode =
+proc typeSectionTypeName(c: PContext; n: PNode): PNode =
   if n.kind == nkPragmaExpr:
-    if n.len == 0: illFormedAst(n)
+    if n.len == 0: illFormedAst(n, c.config)
     result = n.sons[0]
   else:
     result = n
-  if result.kind != nkSym: illFormedAst(n)
+  if result.kind != nkSym: illFormedAst(n, c.config)
 
 
 proc typeSectionLeftSidePass(c: PContext, n: PNode) =
@@ -815,21 +785,21 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) =
   for i in countup(0, sonsLen(n) - 1):
     var a = n.sons[i]
     when defined(nimsuggest):
-      if gCmd == cmdIdeTools:
+      if c.config.cmd == cmdIdeTools:
         inc c.inTypeContext
         suggestStmt(c, a)
         dec c.inTypeContext
     if a.kind == nkCommentStmt: continue
-    if a.kind != nkTypeDef: illFormedAst(a)
-    checkSonsLen(a, 3)
+    if a.kind != nkTypeDef: illFormedAst(a, c.config)
+    checkSonsLen(a, 3, c.config)
     let name = a.sons[0]
     var s: PSym
     if name.kind == nkDotExpr and a[2].kind == nkObjectTy:
-      let pkgName = considerQuotedIdent(name[0])
-      let typName = considerQuotedIdent(name[1])
+      let pkgName = considerQuotedIdent(c.config, name[0])
+      let typName = considerQuotedIdent(c.config, name[1])
       let pkg = c.graph.packageSyms.strTableGet(pkgName)
       if pkg.isNil or pkg.kind != skPackage:
-        localError(name.info, "unknown package name: " & pkgName.s)
+        localError(c.config, name.info, "unknown package name: " & pkgName.s)
       else:
         let typsym = pkg.tab.strTableGet(typName)
         if typsym.isNil:
@@ -843,7 +813,7 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) =
           s = typsym
           addInterfaceDecl(c, s)
         else:
-          localError(name.info, typsym.name.s & " is not a type that can be forwarded")
+          localError(c.config, name.info, typsym.name.s & " is not a type that can be forwarded")
           s = typsym
     else:
       s = semIdentDef(c, name, skType)
@@ -855,7 +825,7 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) =
         # check if the symbol already exists:
         let pkg = c.module.owner
         if not isTopLevel(c) or pkg.isNil:
-          localError(name.info, "only top level types in a package can be 'package'")
+          localError(c.config, name.info, "only top level types in a package can be 'package'")
         else:
           let typsym = pkg.tab.strTableGet(s.name)
           if typsym != nil:
@@ -863,7 +833,7 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) =
               typeCompleted(typsym)
               typsym.info = s.info
             else:
-              localError(name.info, "cannot complete type '" & s.name.s & "' twice; " &
+              localError(c.config, name.info, "cannot complete type '" & s.name.s & "' twice; " &
                       "previous type completion was here: " & $typsym.info)
             s = typsym
       # add it here, so that recursive types are possible:
@@ -874,14 +844,12 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) =
     else:
       a.sons[0] = newSymNode(s)
 
-proc checkCovariantParamsUsages(genericType: PType) =
+proc checkCovariantParamsUsages(c: PContext; genericType: PType) =
   var body = genericType[^1]
 
-  proc traverseSubTypes(t: PType): bool =
-    template error(msg) = localError(genericType.sym.info, msg)
-
+  proc traverseSubTypes(c: PContext; t: PType): bool =
+    template error(msg) = localError(c.config, genericType.sym.info, msg)
     result = false
-
     template subresult(r) =
       let sub = r
       result = result or sub
@@ -890,24 +858,19 @@ proc checkCovariantParamsUsages(genericType: PType) =
     of tyGenericParam:
       t.flags.incl tfWeakCovariant
       return true
-
     of tyObject:
       for field in t.n:
-        subresult traverseSubTypes(field.typ)
-
+        subresult traverseSubTypes(c, field.typ)
     of tyArray:
-      return traverseSubTypes(t[1])
-
+      return traverseSubTypes(c, t[1])
     of tyProc:
       for subType in t.sons:
         if subType != nil:
-          subresult traverseSubTypes(subType)
+          subresult traverseSubTypes(c, subType)
       if result:
-        error("non-invariant type param used in a proc type: " &  $t)
-
+        error("non-invariant type param used in a proc type: " & $t)
     of tySequence:
-      return traverseSubTypes(t[0])
-
+      return traverseSubTypes(c, t[0])
     of tyGenericInvocation:
       let targetBody = t[0]
       for i in 1 ..< t.len:
@@ -928,43 +891,35 @@ proc checkCovariantParamsUsages(genericType: PType) =
                     "' used in a non-contravariant position")
             result = true
         else:
-          subresult traverseSubTypes(param)
-
+          subresult traverseSubTypes(c, param)
     of tyAnd, tyOr, tyNot, tyStatic, tyBuiltInTypeClass, tyCompositeTypeClass:
       error("non-invariant type parameters cannot be used with types such '" & $t & "'")
-
     of tyUserTypeClass, tyUserTypeClassInst:
       error("non-invariant type parameters are not supported in concepts")
-
     of tyTuple:
       for fieldType in t.sons:
-        subresult traverseSubTypes(fieldType)
-
+        subresult traverseSubTypes(c, fieldType)
     of tyPtr, tyRef, tyVar, tyLent:
       if t.base.kind == tyGenericParam: return true
-      return traverseSubTypes(t.base)
-
+      return traverseSubTypes(c, t.base)
     of tyDistinct, tyAlias, tySink:
-      return traverseSubTypes(t.lastSon)
-
+      return traverseSubTypes(c, t.lastSon)
     of tyGenericInst:
-      internalAssert false
-
+      internalAssert c.config, false
     else:
       discard
-
-  discard traverseSubTypes(body)
+  discard traverseSubTypes(c, body)
 
 proc typeSectionRightSidePass(c: PContext, n: PNode) =
   for i in countup(0, sonsLen(n) - 1):
     var a = n.sons[i]
     if a.kind == nkCommentStmt: continue
-    if (a.kind != nkTypeDef): illFormedAst(a)
-    checkSonsLen(a, 3)
-    let name = typeSectionTypeName(a.sons[0])
+    if a.kind != nkTypeDef: illFormedAst(a, c.config)
+    checkSonsLen(a, 3, c.config)
+    let name = typeSectionTypeName(c, a.sons[0])
     var s = name.sym
     if s.magic == mNone and a.sons[2].kind == nkEmpty:
-      localError(a.info, errImplOfXexpected, s.name.s)
+      localError(c.config, a.info, errImplOfXexpected % s.name.s)
     if s.magic != mNone: processMagicType(c, s)
     if a.sons[1].kind != nkEmpty:
       # We have a generic type declaration here. In generic types,
@@ -995,7 +950,7 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
         body.size = -1 # could not be computed properly
         s.typ.sons[sonsLen(s.typ) - 1] = body
         if tfCovariant in s.typ.flags:
-          checkCovariantParamsUsages(s.typ)
+          checkCovariantParamsUsages(c, s.typ)
           # XXX: This is a temporary limitation:
           # The codegen currently produces various failures with
           # generic imported types that have fields, but we need
@@ -1030,8 +985,8 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
       # give anonymous object a dummy symbol:
       var st = s.typ
       if st.kind == tyGenericBody: st = st.lastSon
-      internalAssert st.kind in {tyPtr, tyRef}
-      internalAssert st.lastSon.sym == nil
+      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"),
                               getCurrOwner(c), s.info)
@@ -1039,17 +994,17 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
       st.lastSon.sym = obj
 
 
-proc checkForMetaFields(n: PNode) =
+proc checkForMetaFields(c: PContext; n: PNode) =
   template checkMeta(t) =
     if t != nil and t.isMetaType and tfGenericTypeParam notin t.flags:
-      localError(n.info, errTIsNotAConcreteType, t.typeToString)
+      localError(c.config, n.info, errTIsNotAConcreteType % t.typeToString)
 
   if n.isNil: return
   case n.kind
   of nkRecList, nkRecCase:
-    for s in n: checkForMetaFields(s)
+    for s in n: checkForMetaFields(c, s)
   of nkOfBranch, nkElse:
-    checkForMetaFields(n.lastSon)
+    checkForMetaFields(c, n.lastSon)
   of nkSym:
     let t = n.sym.typ
     case t.kind
@@ -1061,13 +1016,13 @@ proc checkForMetaFields(n: PNode) =
     else:
       checkMeta(t)
   else:
-    internalAssert false
+    internalAssert c.config, false
 
 proc typeSectionFinalPass(c: PContext, n: PNode) =
   for i in countup(0, sonsLen(n) - 1):
     var a = n.sons[i]
     if a.kind == nkCommentStmt: continue
-    let name = typeSectionTypeName(a.sons[0])
+    let name = typeSectionTypeName(c, a.sons[0])
     var s = name.sym
     # compute the type's size and check for illegal recursions:
     if a.sons[1].kind == nkEmpty:
@@ -1087,9 +1042,9 @@ proc typeSectionFinalPass(c: PContext, n: PNode) =
             assert s.typ != nil
             assignType(s.typ, t)
             s.typ.id = t.id     # same id
-      checkConstructedType(s.info, s.typ)
+      checkConstructedType(c.config, s.info, s.typ)
       if s.typ.kind in {tyObject, tyTuple} and not s.typ.n.isNil:
-        checkForMetaFields(s.typ.n)
+        checkForMetaFields(c, s.typ.n)
   instAllTypeBoundOp(c, n.info)
 
 
@@ -1098,14 +1053,14 @@ proc semAllTypeSections(c: PContext; n: PNode): PNode =
     case n.kind
     of nkIncludeStmt:
       for i in 0..<n.len:
-        var f = checkModuleName(n.sons[i])
+        var f = checkModuleName(c.config, n.sons[i])
         if f != InvalidFileIDX:
-          if containsOrIncl(c.includedFiles, f):
-            localError(n.info, errRecursiveDependencyX, f.toFilename)
+          if containsOrIncl(c.includedFiles, f.int):
+            localError(c.config, n.info, errRecursiveDependencyX % f.toFilename)
           else:
             let code = gIncludeFile(c.graph, c.module, f, c.cache)
             gatherStmts c, code, result
-            excl(c.includedFiles, f)
+            excl(c.includedFiles, f.int)
     of nkStmtList:
       for i in 0 ..< n.len:
         gatherStmts(c, n.sons[i], result)
@@ -1157,12 +1112,12 @@ proc semParamList(c: PContext, n, genericParams: PNode, s: PSym) =
   s.typ = semProcTypeNode(c, n, genericParams, nil, s.kind)
   if s.kind notin {skMacro, skTemplate}:
     if s.typ.sons[0] != nil and s.typ.sons[0].kind == tyStmt:
-      localError(n.info, errGenerated, "invalid return type: 'stmt'")
+      localError(c.config, n.info, "invalid return type: 'stmt'")
 
 proc addParams(c: PContext, n: PNode, kind: TSymKind) =
   for i in countup(1, sonsLen(n)-1):
     if n.sons[i].kind == nkSym: addParamOrResult(c, n.sons[i].sym, kind)
-    else: illFormedAst(n)
+    else: illFormedAst(n, c.config)
 
 proc semBorrow(c: PContext, n: PNode, s: PSym) =
   # search for the correct alias:
@@ -1171,7 +1126,7 @@ proc semBorrow(c: PContext, n: PNode, s: PSym) =
     # store the alias:
     n.sons[bodyPos] = newSymNode(b)
   else:
-    localError(n.info, errNoSymbolToBorrowFromFound)
+    localError(c.config, n.info, errNoSymbolToBorrowFromFound)
 
 proc addResult(c: PContext, t: PType, info: TLineInfo, owner: TSymKind) =
   if t != nil:
@@ -1194,7 +1149,7 @@ proc lookupMacro(c: PContext, n: PNode): PSym =
     result = n.sym
     if result.kind notin {skMacro, skTemplate}: result = nil
   else:
-    result = searchInScopes(c, considerQuotedIdent(n), {skMacro, skTemplate})
+    result = searchInScopes(c, considerQuotedIdent(c.config, n), {skMacro, skTemplate})
 
 proc semProcAnnotation(c: PContext, prc: PNode;
                        validPragmas: TSpecialWords): PNode =
@@ -1206,11 +1161,11 @@ 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(prc.sons[namePos]).s == "()":
+        if considerQuotedIdent(c.config, prc.sons[namePos]).s == "()":
           prc.sons[namePos] = newIdentNode(c.cache.idDelegator, prc.info)
           prc.sons[pragmasPos] = copyExcept(n, i)
         else:
-          localError(prc.info, errOnlyACallOpCanBeDelegator)
+          localError(c.config, prc.info, "only a call operator can be a delegator")
       continue
     elif sfCustomPragma in m.flags:
       continue # semantic check for custom pragma happens later in semProcAux
@@ -1255,7 +1210,7 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode =
   result = semProcAnnotation(c, n, lambdaPragmas)
   if result != nil: return result
   result = n
-  checkSonsLen(n, bodyPos + 1)
+  checkSonsLen(n, bodyPos + 1, c.config)
   var s: PSym
   if n[namePos].kind != nkSym:
     s = newSym(skProc, c.cache.idAnon, getCurrOwner(c), n.info)
@@ -1272,9 +1227,6 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode =
     gp = newNodeI(nkGenericParams, n.info)
 
   if n.sons[paramsPos].kind != nkEmpty:
-    #if n.kind == nkDo and not experimentalMode(c):
-    #  localError(n.sons[paramsPos].info,
-    #      "use the {.experimental.} pragma to enable 'do' with parameters")
     semParamList(c, n.sons[paramsPos], gp, s)
     # paramsTypeCheck(c, s.typ)
     if sonsLen(gp) > 0 and n.sons[genericParamsPos].kind == nkEmpty:
@@ -1284,10 +1236,10 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode =
     s.typ = newProcType(c, n.info)
   if n.sons[pragmasPos].kind != nkEmpty:
     pragma(c, s, n.sons[pragmasPos], lambdaPragmas)
-  s.options = gOptions
+  s.options = c.config.options
   if n.sons[bodyPos].kind != nkEmpty:
     if sfImportc in s.flags:
-      localError(n.sons[bodyPos].info, errImplOfXNotAllowed, s.name.s)
+      localError(c.config, n.sons[bodyPos].info, errImplOfXNotAllowed % s.name.s)
     #if efDetermineType notin flags:
     # XXX not good enough; see tnamedparamanonproc.nim
     if gp.len == 0 or (gp.len == 1 and tfRetType in gp[0].typ.flags):
@@ -1295,13 +1247,13 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode =
       addResult(c, s.typ.sons[0], n.info, skProc)
       addResultNode(c, n)
       let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos]))
-      n.sons[bodyPos] = transformBody(c.module, semBody, s)
+      n.sons[bodyPos] = transformBody(c.graph, c.module, semBody, s)
       popProcCon(c)
     elif efOperand notin flags:
-      localError(n.info, errGenericLambdaNotAllowed)
+      localError(c.config, n.info, errGenericLambdaNotAllowed)
     sideEffectsCheck(c, s)
   else:
-    localError(n.info, errImplOfXexpected, s.name.s)
+    localError(c.config, n.info, errImplOfXexpected % s.name.s)
   closeScope(c)           # close scope for parameters
   popOwner(c)
   result.typ = s.typ
@@ -1326,7 +1278,7 @@ proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode =
   for i in 1..<params.len:
     if params[i].typ.kind in {tyTypeDesc, tyGenericParam,
                               tyFromExpr}+tyTypeClasses:
-      localError(params[i].info, "cannot infer type of parameter: " &
+      localError(c.config, params[i].info, "cannot infer type of parameter: " &
                  params[i].sym.name.s)
     #params[i].sym.owner = s
   openScope(c)
@@ -1336,7 +1288,7 @@ proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode =
   addResult(c, n.typ.sons[0], n.info, skProc)
   addResultNode(c, n)
   let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos]))
-  n.sons[bodyPos] = transformBody(c.module, semBody, s)
+  n.sons[bodyPos] = transformBody(c.graph, c.module, semBody, s)
   popProcCon(c)
   popOwner(c)
   closeScope(c)
@@ -1368,27 +1320,26 @@ proc maybeAddResult(c: PContext, s: PSym, n: PNode) =
 
 proc semOverride(c: PContext, s: PSym, n: PNode) =
   case s.name.s.normalize
-  of "destroy", "=destroy":
-    if newDestructors:
-      let t = s.typ
-      var noError = false
-      if t.len == 2 and t.sons[0] == nil and t.sons[1].kind == tyVar:
-        var obj = t.sons[1].sons[0]
-        while true:
-          incl(obj.flags, tfHasAsgn)
-          if obj.kind in {tyGenericBody, tyGenericInst}: obj = obj.lastSon
-          elif obj.kind == tyGenericInvocation: obj = obj.sons[0]
-          else: break
-        if obj.kind in {tyObject, tyDistinct}:
-          if obj.destructor.isNil:
-            obj.destructor = s
-          else:
-            localError(n.info, errGenerated,
-              "cannot bind another '" & s.name.s & "' to: " & typeToString(obj))
-          noError = true
-      if not noError and sfSystemModule notin s.owner.flags:
-        localError(n.info, errGenerated,
-          "signature for '" & s.name.s & "' must be proc[T: object](x: var T)")
+  of "=destroy":
+    let t = s.typ
+    var noError = false
+    if t.len == 2 and t.sons[0] == nil and t.sons[1].kind == tyVar:
+      var obj = t.sons[1].sons[0]
+      while true:
+        incl(obj.flags, tfHasAsgn)
+        if obj.kind in {tyGenericBody, tyGenericInst}: obj = obj.lastSon
+        elif obj.kind == tyGenericInvocation: obj = obj.sons[0]
+        else: break
+      if obj.kind in {tyObject, tyDistinct}:
+        if obj.destructor.isNil:
+          obj.destructor = s
+        else:
+          localError(c.config, n.info, errGenerated,
+            "cannot bind another '" & s.name.s & "' to: " & typeToString(obj))
+        noError = true
+    if not noError and sfSystemModule notin s.owner.flags:
+      localError(c.config, n.info, errGenerated,
+        "signature for '" & s.name.s & "' must be proc[T: object](x: var T)")
     incl(s.flags, sfUsed)
   of "deepcopy", "=deepcopy":
     if s.typ.len == 2 and
@@ -1404,13 +1355,13 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
       if t.kind in {tyObject, tyDistinct, tyEnum}:
         if t.deepCopy.isNil: t.deepCopy = s
         else:
-          localError(n.info, errGenerated,
+          localError(c.config, n.info, errGenerated,
                      "cannot bind another 'deepCopy' to: " & typeToString(t))
       else:
-        localError(n.info, errGenerated,
+        localError(c.config, n.info, errGenerated,
                    "cannot bind 'deepCopy' to: " & typeToString(t))
     else:
-      localError(n.info, errGenerated,
+      localError(c.config, n.info, errGenerated,
                  "signature for 'deepCopy' must be proc[T: ptr|ref](x: T): T")
     incl(s.flags, sfUsed)
   of "=", "=sink":
@@ -1435,15 +1386,15 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
         if opr[].isNil:
           opr[] = s
         else:
-          localError(n.info, errGenerated,
+          localError(c.config, n.info, errGenerated,
                      "cannot bind another '" & s.name.s & "' to: " & typeToString(obj))
         return
     if sfSystemModule notin s.owner.flags:
-      localError(n.info, errGenerated,
+      localError(c.config, n.info, errGenerated,
                 "signature for '" & s.name.s & "' must be proc[T: object](x: var T; y: T)")
   else:
     if sfOverriden in s.flags:
-      localError(n.info, errGenerated,
+      localError(c.config, n.info, errGenerated,
                  "'destroy' or 'deepCopy' expected for 'override'")
 
 proc cursorInProcAux(n: PNode): bool =
@@ -1486,7 +1437,7 @@ proc semMethodPrototype(c: PContext; s: PSym; n: PNode) =
           foundObj = true
           x.methods.safeAdd((col,s))
     if not foundObj:
-      message(n.info, warnDeprecated, "generic method not attachable to object type")
+      message(c.config, n.info, warnDeprecated, "generic method not attachable to object type")
   else:
     # why check for the body? bug #2400 has none. Checking for sfForward makes
     # no sense either.
@@ -1494,7 +1445,7 @@ proc semMethodPrototype(c: PContext; s: PSym; n: PNode) =
     if hasObjParam(s):
       methodDef(c.graph, s, fromCache=false)
     else:
-      localError(n.info, errXNeedsParamObjectType, "method")
+      localError(c.config, n.info, "'method' needs a parameter that has an object type")
 
 proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
                 validPragmas: TSpecialWords,
@@ -1502,7 +1453,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
   result = semProcAnnotation(c, n, validPragmas)
   if result != nil: return result
   result = n
-  checkSonsLen(n, bodyPos + 1)
+  checkSonsLen(n, bodyPos + 1, c.config)
   var s: PSym
   var typeIsDetermined = false
   var isAnon = false
@@ -1591,10 +1542,10 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
       # XXX This needs more checks eventually, for example that external
       # linking names do agree:
       if proto.typ.callConv != s.typ.callConv or proto.typ.flags < s.typ.flags:
-        localError(n.sons[pragmasPos].info, errPragmaOnlyInHeaderOfProcX,
-          "'" & proto.name.s & "' from " & $proto.info)
+        localError(c.config, n.sons[pragmasPos].info, errPragmaOnlyInHeaderOfProcX %
+          ("'" & proto.name.s & "' from " & $proto.info))
     if sfForward notin proto.flags:
-      wrongRedefinition(n.info, proto.name.s)
+      wrongRedefinition(c, n.info, proto.name.s)
     excl(proto.flags, sfForward)
     closeScope(c)         # close scope with wrong parameter symbols
     openScope(c)          # open scope for old (correct) parameter symbols
@@ -1607,30 +1558,32 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
     n.sons[genericParamsPos] = proto.ast.sons[genericParamsPos]
     n.sons[paramsPos] = proto.ast.sons[paramsPos]
     n.sons[pragmasPos] = proto.ast.sons[pragmasPos]
-    if n.sons[namePos].kind != nkSym: internalError(n.info, "semProcAux")
+    if n.sons[namePos].kind != nkSym: internalError(c.config, n.info, "semProcAux")
     n.sons[namePos].sym = proto
-    if importantComments() and not isNil(proto.ast.comment):
+    if importantComments(c.config) and not isNil(proto.ast.comment):
       n.comment = proto.ast.comment
     proto.ast = n             # needed for code generation
     popOwner(c)
     pushOwner(c, s)
-  s.options = gOptions
+  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 not experimentalMode(c) and not newDestructors:
-      message(n.info, warnDeprecated, "overloaded '.' and '()' operators are now .experimental; " & s.name.s)
-    elif s.name.s == "()" and not experimentalMode(c):
-      message(n.info, warnDeprecated, "overloaded '()' operators are now .experimental; " & s.name.s)
+    if s.name.s in [".", ".()", ".="] and {destructor, dotOperators} * c.features == {}:
+      localError(c.config, n.info, "the overloaded " & s.name.s &
+        " operator has to be enabled with {.experimental: \"dotOperators\".}")
+    elif s.name.s == "()" and callOperator notin c.features:
+      localError(c.config, n.info, "the overloaded " & s.name.s &
+        " operator has to be enabled with {.experimental: \"callOperator\".}")
 
   if n.sons[bodyPos].kind != nkEmpty:
     # for DLL generation it is annoying to check for sfImportc!
     if sfBorrow in s.flags:
-      localError(n.sons[bodyPos].info, errImplOfXNotAllowed, s.name.s)
+      localError(c.config, n.sons[bodyPos].info, errImplOfXNotAllowed % s.name.s)
     let usePseudoGenerics = kind in {skMacro, skTemplate}
     # Macros and Templates can have generic parameters, but they are
     # only used for overload resolution (there is no instantiation of
     # the symbol, so we must process the body now)
-    if not usePseudoGenerics and gIdeCmd in {ideSug, ideCon} and not
+    if not usePseudoGenerics and c.config.ideCmd in {ideSug, ideCon} and not
         cursorInProc(n.sons[bodyPos]):
       discard "speed up nimsuggest"
       if s.kind == skMethod: semMethodPrototype(c, s, n)
@@ -1648,7 +1601,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
           let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos]))
           # unfortunately we cannot skip this step when in 'system.compiles'
           # context as it may even be evaluated in 'system.compiles':
-          n.sons[bodyPos] = transformBody(c.module, semBody, s)
+          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))
@@ -1664,7 +1617,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
       popProcCon(c)
   else:
     if s.kind == skMethod: semMethodPrototype(c, s, n)
-    if proto != nil: localError(n.info, errImplOfXexpected, proto.name.s)
+    if proto != nil: localError(c.config, n.info, errImplOfXexpected % proto.name.s)
     if {sfImportc, sfBorrow} * s.flags == {} and s.magic == mNone:
       incl(s.flags, sfForward)
     elif sfBorrow in s.flags: semBorrow(c, n, s)
@@ -1679,7 +1632,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
     result.typ = s.typ
   if isTopLevel(c) and s.kind != skIterator and
       s.typ.callConv == ccClosure:
-    localError(s.info, "'.closure' calling convention for top level routines is invalid")
+    localError(c.config, s.info, "'.closure' calling convention for top level routines is invalid")
 
 proc determineType(c: PContext, s: PSym) =
   if s.typ != nil: return
@@ -1702,9 +1655,9 @@ proc semIterator(c: PContext, n: PNode): PNode =
   var s = result.sons[namePos].sym
   var t = s.typ
   if t.sons[0] == nil and s.typ.callConv != ccClosure:
-    localError(n.info, errXNeedsReturnType, "iterator")
+    localError(c.config, n.info, "iterator needs a return type")
   if isAnon and s.typ.callConv == ccInline:
-    localError(n.info, "inline iterators are not first-class / cannot be assigned to variables")
+    localError(c.config, n.info, "inline iterators are not first-class / cannot be assigned to variables")
   # iterators are either 'inline' or 'closure'; for backwards compatibility,
   # we require first class iterators to be marked with 'closure' explicitly
   # -- at least for 0.9.2.
@@ -1712,13 +1665,8 @@ proc semIterator(c: PContext, n: PNode): PNode =
     incl(s.typ.flags, tfCapturesEnv)
   else:
     s.typ.callConv = ccInline
-  when false:
-    if s.typ.callConv != ccInline:
-      s.typ.callConv = ccClosure
-      # and they always at least use the 'env' for the state field:
-      incl(s.typ.flags, tfCapturesEnv)
   if n.sons[bodyPos].kind == nkEmpty and s.magic == mNone:
-    localError(n.info, errImplOfXexpected, s.name.s)
+    localError(c.config, n.info, errImplOfXexpected % s.name.s)
 
 proc semProc(c: PContext, n: PNode): PNode =
   result = semProcAux(c, n, skProc, procPragmas)
@@ -1727,7 +1675,7 @@ proc semFunc(c: PContext, n: PNode): PNode =
   result = semProcAux(c, n, skFunc, procPragmas)
 
 proc semMethod(c: PContext, n: PNode): PNode =
-  if not isTopLevel(c): localError(n.info, errXOnlyAtModuleScope, "method")
+  if not isTopLevel(c): localError(c.config, n.info, errXOnlyAtModuleScope % "method")
   result = semProcAux(c, n, skMethod, methodPragmas)
   # macros can transform converters to nothing:
   if namePos >= result.safeLen: return result
@@ -1748,8 +1696,8 @@ proc semMethod(c: PContext, n: PNode): PNode =
       else: disp.ast[resultPos].sym.typ = ret
 
 proc semConverterDef(c: PContext, n: PNode): PNode =
-  if not isTopLevel(c): localError(n.info, errXOnlyAtModuleScope, "converter")
-  checkSonsLen(n, bodyPos + 1)
+  if not isTopLevel(c): localError(c.config, n.info, errXOnlyAtModuleScope % "converter")
+  checkSonsLen(n, bodyPos + 1, c.config)
   result = semProcAux(c, n, skConverter, converterPragmas)
   # macros can transform converters to nothing:
   if namePos >= result.safeLen: return result
@@ -1759,12 +1707,12 @@ proc semConverterDef(c: PContext, n: PNode): PNode =
   if result.kind != nkConverterDef: return
   var s = result.sons[namePos].sym
   var t = s.typ
-  if t.sons[0] == nil: localError(n.info, errXNeedsReturnType, "converter")
-  if sonsLen(t) != 2: localError(n.info, errXRequiresOneArgument, "converter")
+  if t.sons[0] == nil: localError(c.config, n.info, errXNeedsReturnType % "converter")
+  if sonsLen(t) != 2: localError(c.config, n.info, "a converter takes exactly one argument")
   addConverter(c, s)
 
 proc semMacroDef(c: PContext, n: PNode): PNode =
-  checkSonsLen(n, bodyPos + 1)
+  checkSonsLen(n, bodyPos + 1, c.config)
   result = semProcAux(c, n, skMacro, macroPragmas)
   # macros can transform macros to nothing:
   if namePos >= result.safeLen: return result
@@ -1779,21 +1727,21 @@ proc semMacroDef(c: PContext, n: PNode): PNode =
     let param = t.n.sons[i].sym
     if param.typ.kind != tyExpr: allUntyped = false
   if allUntyped: incl(s.flags, sfAllUntyped)
-  if t.sons[0] == nil: localError(n.info, errXNeedsReturnType, "macro")
+  if t.sons[0] == nil: localError(c.config, n.info, "macro needs a return type")
   if n.sons[bodyPos].kind == nkEmpty:
-    localError(n.info, errImplOfXexpected, s.name.s)
+    localError(c.config, n.info, errImplOfXexpected % s.name.s)
 
 proc evalInclude(c: PContext, n: PNode): PNode =
   result = newNodeI(nkStmtList, n.info)
   addSon(result, n)
   for i in countup(0, sonsLen(n) - 1):
-    var f = checkModuleName(n.sons[i])
+    var f = checkModuleName(c.config, n.sons[i])
     if f != InvalidFileIDX:
-      if containsOrIncl(c.includedFiles, f):
-        localError(n.info, errRecursiveDependencyX, f.toFilename)
+      if containsOrIncl(c.includedFiles, f.int):
+        localError(c.config, n.info, errRecursiveDependencyX % f.toFilename)
       else:
         addSon(result, semStmt(c, gIncludeFile(c.graph, c.module, f, c.cache)))
-        excl(c.includedFiles, f)
+        excl(c.includedFiles, f.int)
 
 proc setLine(n: PNode, info: TLineInfo) =
   for i in 0 ..< safeLen(n): setLine(n.sons[i], info)
@@ -1817,19 +1765,13 @@ proc semPragmaBlock(c: PContext, n: PNode): PNode =
 proc semStaticStmt(c: PContext, n: PNode): PNode =
   #echo "semStaticStmt"
   #writeStackTrace()
+  inc c.inStaticContext
   let a = semStmt(c, n.sons[0])
+  dec c.inStaticContext
   n.sons[0] = a
-  evalStaticStmt(c.module, c.cache, a, c.p.owner)
+  evalStaticStmt(c.module, c.cache, c.graph, a, c.p.owner)
   result = newNodeI(nkDiscardStmt, n.info, 1)
   result.sons[0] = emptyNode
-  when false:
-    result = evalStaticStmt(c.module, a, c.p.owner)
-    if result.isNil:
-      LocalError(n.info, errCannotInterpretNodeX, renderTree(n))
-      result = emptyNode
-    elif result.kind == nkEmpty:
-      result = newNodeI(nkDiscardStmt, n.info, 1)
-      result.sons[0] = emptyNode
 
 proc usesResult(n: PNode): bool =
   # nkStmtList(expr) properly propagates the void context,
@@ -1848,7 +1790,7 @@ proc inferConceptStaticParam(c: PContext, inferred, n: PNode) =
   var typ = inferred.typ
   let res = semConstExpr(c, n)
   if not sameType(res.typ, typ.base):
-    localError(n.info,
+    localError(c.config, n.info,
       "cannot infer the concept parameter '%s', due to a type mismatch. " &
       "attempt to equate '%s' and '%s'.",
       [inferred.renderTree, $res.typ, $typ.base])
@@ -1871,80 +1813,47 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
   #                                         nkNilLit, nkEmpty}:
   #  dec last
   for i in countup(0, length - 1):
-    let k = n.sons[i].kind
-    case k
-    of nkFinally, nkExceptBranch:
-      # stand-alone finally and except blocks are
-      # transformed into regular try blocks:
-      #
-      # var f = fopen("somefile") | var f = fopen("somefile")
-      # finally: fclose(f)        | try:
-      # ...                       |   ...
-      #                           | finally:
-      #                           |   fclose(f)
-      var deferPart: PNode
-      if k == nkDefer:
-        deferPart = newNodeI(nkFinally, n.sons[i].info)
-        deferPart.add n.sons[i].sons[0]
-      elif k == nkFinally:
-        message(n.info, warnDeprecated,
-                "use 'defer'; standalone 'finally'")
-        deferPart = n.sons[i]
-      else:
-        message(n.info, warnDeprecated,
-                "use an explicit 'try'; standalone 'except'")
-        deferPart = n.sons[i]
-      var tryStmt = newNodeI(nkTryStmt, n.sons[i].info)
-      var body = newNodeI(nkStmtList, n.sons[i].info)
-      if i < n.sonsLen - 1:
-        body.sons = n.sons[(i+1)..n.len-1]
-      tryStmt.addSon(body)
-      tryStmt.addSon(deferPart)
-      n.sons[i] = semTry(c, tryStmt)
-      n.sons.setLen(i+1)
+    var expr = semExpr(c, n.sons[i], flags)
+    n.sons[i] = expr
+    if c.matchedConcept != nil and expr.typ != nil and
+        (nfFromTemplate notin n.flags or i != last):
+      case expr.typ.kind
+      of tyBool:
+        if expr.kind == nkInfix and
+            expr[0].kind == nkSym and
+            expr[0].sym.name.s == "==":
+          if expr[1].typ.isUnresolvedStatic:
+            inferConceptStaticParam(c, expr[1], expr[2])
+            continue
+          elif expr[2].typ.isUnresolvedStatic:
+            inferConceptStaticParam(c, expr[2], expr[1])
+            continue
+
+        let verdict = semConstExpr(c, n[i])
+        if verdict.intVal == 0:
+          localError(c.config, result.info, "concept predicate failed")
+      of tyUnknown: continue
+      else: discard
+    if n.sons[i].typ == enforceVoidContext: #or usesResult(n.sons[i]):
+      voidContext = true
+      n.typ = enforceVoidContext
+    if i == last and (length == 1 or efWantValue in flags):
       n.typ = n.sons[i].typ
-      return
+      if not isEmptyType(n.typ): n.kind = nkStmtListExpr
+    elif i != last or voidContext:
+      discardCheck(c, n.sons[i])
     else:
-      var expr = semExpr(c, n.sons[i], flags)
-      n.sons[i] = expr
-      if c.matchedConcept != nil and expr.typ != nil and
-         (nfFromTemplate notin n.flags or i != last):
-        case expr.typ.kind
-        of tyBool:
-          if expr.kind == nkInfix and
-             expr[0].kind == nkSym and
-             expr[0].sym.name.s == "==":
-            if expr[1].typ.isUnresolvedStatic:
-              inferConceptStaticParam(c, expr[1], expr[2])
-              continue
-            elif expr[2].typ.isUnresolvedStatic:
-              inferConceptStaticParam(c, expr[2], expr[1])
-              continue
-
-          let verdict = semConstExpr(c, n[i])
-          if verdict.intVal == 0:
-            localError(result.info, "concept predicate failed")
-        of tyUnknown: continue
-        else: discard
-      if n.sons[i].typ == enforceVoidContext: #or usesResult(n.sons[i]):
-        voidContext = true
-        n.typ = 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
-      elif i != last or voidContext:
-        discardCheck(c, n.sons[i])
-      else:
-        n.typ = n.sons[i].typ
-        if not isEmptyType(n.typ): n.kind = nkStmtListExpr
-      if n.sons[i].kind in LastBlockStmts or
-         n.sons[i].kind in nkCallKinds and n.sons[i][0].kind == nkSym and sfNoReturn in n.sons[i][0].sym.flags:
-        for j in countup(i + 1, length - 1):
-          case n.sons[j].kind
-          of nkPragma, nkCommentStmt, nkNilLit, nkEmpty, nkBlockExpr,
-             nkBlockStmt, nkState: discard
-          else: localError(n.sons[j].info, errStmtInvalidAfterReturn)
-      else: discard
+      n.typ = n.sons[i].typ
+      if not isEmptyType(n.typ): n.kind = nkStmtListExpr
+    if n.sons[i].kind in LastBlockStmts or
+        n.sons[i].kind in nkCallKinds and n.sons[i][0].kind == nkSym and
+        sfNoReturn in n.sons[i][0].sym.flags:
+      for j in countup(i + 1, length - 1):
+        case n.sons[j].kind
+        of nkPragma, nkCommentStmt, nkNilLit, nkEmpty, nkBlockExpr,
+            nkBlockStmt, nkState: discard
+        else: localError(c.config, n.sons[j].info, "unreachable statement after 'return'")
+    else: discard
 
   if result.len == 1 and
      # concept bodies should be preserved as a stmt list:
@@ -1960,15 +1869,6 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
         not (result.comment[0] == '#' and result.comment[1] == '#'):
       # it is an old-style comment statement: we replace it with 'discard ""':
       prettybase.replaceComment(result.info)
-  when false:
-    # a statement list (s; e) has the type 'e':
-    if result.kind == nkStmtList and result.len > 0:
-      var lastStmt = lastSon(result)
-      if lastStmt.kind != nkNilLit and not implicitlyDiscardable(lastStmt):
-        result.typ = lastStmt.typ
-        #localError(lastStmt.info, errGenerated,
-        #  "Last expression must be explicitly returned if it " &
-        #  "is discardable or discarded")
 
 proc semStmt(c: PContext, n: PNode): PNode =
   # now: simply an alias:
diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim
index 454dadec0..352bc5c6b 100644
--- a/compiler/semtempl.nim
+++ b/compiler/semtempl.nim
@@ -26,6 +26,9 @@ discard """
   a way to achieve lexical scoping at compile time.
 """
 
+const
+  errImplOfXNotAllowed = "implementation of '$1' is not allowed"
+
 type
   TSymBinding = enum
     spNone, spGenSym, spInject
@@ -60,7 +63,7 @@ proc symChoice(c: PContext, n: PNode, s: PSym, r: TSymChoiceRule): PNode =
     # (s.kind notin routineKinds or s.magic != mNone):
     # for instance 'nextTry' is both in tables.nim and astalgo.nim ...
     result = newSymNode(s, n.info)
-    markUsed(n.info, s, c.graph.usageSym)
+    markUsed(c.config, n.info, s, c.graph.usageSym)
   else:
     # semantic checking requires a type; ``fitNode`` deals with it
     # appropriately
@@ -91,20 +94,20 @@ proc semBindStmt(c: PContext, n: PNode, toBind: var IntSet): PNode =
       else:
         for x in items(sc): toBind.incl(x.sym.id)
     else:
-      illFormedAst(a)
+      illFormedAst(a, c.config)
   result = newNodeI(nkEmpty, n.info)
 
 proc semMixinStmt(c: PContext, n: PNode, toMixin: var IntSet): PNode =
   for i in 0 ..< n.len:
-    toMixin.incl(considerQuotedIdent(n.sons[i]).id)
+    toMixin.incl(considerQuotedIdent(c.config, n.sons[i]).id)
   result = newNodeI(nkEmpty, n.info)
 
-proc replaceIdentBySym(n: var PNode, s: PNode) =
+proc replaceIdentBySym(c: PContext; n: var PNode, s: PNode) =
   case n.kind
-  of nkPostfix: replaceIdentBySym(n.sons[1], s)
-  of nkPragmaExpr: replaceIdentBySym(n.sons[0], s)
+  of nkPostfix: replaceIdentBySym(c, n.sons[1], s)
+  of nkPragmaExpr: replaceIdentBySym(c, n.sons[0], s)
   of nkIdent, nkAccQuoted, nkSym: n = s
-  else: illFormedAst(n)
+  else: illFormedAst(n, c.config)
 
 type
   TemplCtx = object
@@ -129,7 +132,7 @@ proc getIdentNode(c: var TemplCtx, n: PNode): PNode =
         result = newSymNode(s, n.info)
   of nkAccQuoted, nkSym: result = n
   else:
-    illFormedAst(n)
+    illFormedAst(n, c.c.config)
     result = n
 
 proc isTemplParam(c: TemplCtx, n: PNode): bool {.inline.} =
@@ -163,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(n), c.owner, n.info)
+  result = newSym(kind, considerQuotedIdent(c.c.config, n), c.owner, n.info)
   incl(result.flags, sfGenSym)
   incl(result.flags, sfShadowed)
 
@@ -184,12 +187,12 @@ proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) =
         n = onlyReplaceParams(c, n)
         return
       else:
-        illFormedAst(x)
+        illFormedAst(x, c.c.config)
     let ident = getIdentNode(c, x)
     if not isTemplParam(c, ident):
       c.toInject.incl(x.ident.id)
     else:
-      replaceIdentBySym(n, ident)
+      replaceIdentBySym(c.c, n, ident)
   else:
     let ident = getIdentNode(c, n)
     if not isTemplParam(c, ident):
@@ -203,17 +206,17 @@ 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 ident)
+      let s = localSearchInScope(c.c, considerQuotedIdent(c.c.config, ident))
       if s != nil and s.owner == c.owner and sfGenSym in s.flags:
         styleCheckUse(n.info, s)
-        replaceIdentBySym(n, newSymNode(s, n.info))
+        replaceIdentBySym(c.c, n, newSymNode(s, n.info))
       else:
         let local = newGenSym(k, ident, c)
         addPrelimDecl(c.c, local)
         styleCheckDef(n.info, local)
-        replaceIdentBySym(n, newSymNode(local, n.info))
+        replaceIdentBySym(c.c, n, newSymNode(local, n.info))
     else:
-      replaceIdentBySym(n, ident)
+      replaceIdentBySym(c.c, n, ident)
 
 proc semTemplSymbol(c: PContext, n: PNode, s: PSym): PNode =
   incl(s.flags, sfUsed)
@@ -249,7 +252,7 @@ proc semRoutineInTemplName(c: var TemplCtx, n: PNode): PNode =
 
 proc semRoutineInTemplBody(c: var TemplCtx, n: PNode, k: TSymKind): PNode =
   result = n
-  checkSonsLen(n, bodyPos + 1)
+  checkSonsLen(n, bodyPos + 1, c.c.config)
   # routines default to 'inject':
   if n.kind notin nkLambdaKinds and symBinding(n.sons[pragmasPos]) == spGenSym:
     let ident = getIdentNode(c, n.sons[namePos])
@@ -281,8 +284,8 @@ proc semTemplSomeDecl(c: var TemplCtx, n: PNode, symKind: TSymKind; start=0) =
   for i in countup(start, sonsLen(n) - 1):
     var a = n.sons[i]
     if a.kind == nkCommentStmt: continue
-    if (a.kind != nkIdentDefs) and (a.kind != nkVarTuple): illFormedAst(a)
-    checkMinSonsLen(a, 3)
+    if (a.kind != nkIdentDefs) and (a.kind != nkVarTuple): illFormedAst(a, c.c.config)
+    checkMinSonsLen(a, 3, c.c.config)
     var L = sonsLen(a)
     when defined(nimsuggest):
       inc c.c.inTypeContext
@@ -354,7 +357,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
     n.sons[0] = semTemplBody(c, n.sons[0])
     for i in countup(1, sonsLen(n)-1):
       var a = n.sons[i]
-      checkMinSonsLen(a, 1)
+      checkMinSonsLen(a, 1, c.c.config)
       var L = sonsLen(a)
       for j in countup(0, L-2):
         a.sons[j] = semTemplBody(c, a.sons[j])
@@ -371,7 +374,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
     closeScope(c)
     closeScope(c)
   of nkBlockStmt, nkBlockExpr, nkBlockType:
-    checkSonsLen(n, 2)
+    checkSonsLen(n, 2, c.c.config)
     openScope(c)
     if n.sons[0].kind != nkEmpty:
       addLocalDecl(c, n.sons[0], skLabel)
@@ -384,11 +387,11 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
     n.sons[1] = semTemplBody(c, n.sons[1])
     closeScope(c)
   of nkTryStmt:
-    checkMinSonsLen(n, 2)
+    checkMinSonsLen(n, 2, c.c.config)
     n.sons[0] = semTemplBodyScope(c, n.sons[0])
     for i in countup(1, sonsLen(n)-1):
       var a = n.sons[i]
-      checkMinSonsLen(a, 1)
+      checkMinSonsLen(a, 1, c.c.config)
       var L = sonsLen(a)
       openScope(c)
       for j in countup(0, L-2):
@@ -402,15 +405,15 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
   of nkVarSection: semTemplSomeDecl(c, n, skVar)
   of nkLetSection: semTemplSomeDecl(c, n, skLet)
   of nkFormalParams:
-    checkMinSonsLen(n, 1)
+    checkMinSonsLen(n, 1, c.c.config)
     n.sons[0] = semTemplBody(c, n.sons[0])
     semTemplSomeDecl(c, n, skParam, 1)
   of nkConstSection:
     for i in countup(0, sonsLen(n) - 1):
       var a = n.sons[i]
       if a.kind == nkCommentStmt: continue
-      if (a.kind != nkConstDef): illFormedAst(a)
-      checkSonsLen(a, 3)
+      if (a.kind != nkConstDef): illFormedAst(a, c.c.config)
+      checkSonsLen(a, 3, c.c.config)
       addLocalDecl(c, a.sons[0], skConst)
       a.sons[1] = semTemplBody(c, a.sons[1])
       a.sons[2] = semTemplBody(c, a.sons[2])
@@ -418,14 +421,14 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
     for i in countup(0, sonsLen(n) - 1):
       var a = n.sons[i]
       if a.kind == nkCommentStmt: continue
-      if (a.kind != nkTypeDef): illFormedAst(a)
-      checkSonsLen(a, 3)
+      if (a.kind != nkTypeDef): illFormedAst(a, c.c.config)
+      checkSonsLen(a, 3, c.c.config)
       addLocalDecl(c, a.sons[0], skType)
     for i in countup(0, sonsLen(n) - 1):
       var a = n.sons[i]
       if a.kind == nkCommentStmt: continue
-      if (a.kind != nkTypeDef): illFormedAst(a)
-      checkSonsLen(a, 3)
+      if (a.kind != nkTypeDef): illFormedAst(a, c.c.config)
+      checkSonsLen(a, 3, c.c.config)
       if a.sons[1].kind != nkEmpty:
         openScope(c)
         a.sons[1] = semTemplBody(c, a.sons[1])
@@ -468,7 +471,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
     for i in 0 ..< n.len: result.add(n[i])
     result = semTemplBodySons(c, result)
   of nkAsgn, nkFastAsgn:
-    checkSonsLen(n, 2)
+    checkSonsLen(n, 2, c.c.config)
     let a = n.sons[0]
     let b = n.sons[1]
 
@@ -610,9 +613,9 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
   result = n
   if sfCustomPragma in s.flags:
     if n.sons[bodyPos].kind != nkEmpty:
-      localError(n.sons[bodyPos].info, errImplOfXNotAllowed, s.name.s)
+      localError(c.config, n.sons[bodyPos].info, errImplOfXNotAllowed % s.name.s)
   elif n.sons[bodyPos].kind == nkEmpty:
-    localError(n.info, errImplOfXexpected, s.name.s)
+    localError(c.config, n.info, "implementation of '$1' expected" % s.name.s)
   var proto = searchForProc(c, c.currentScope, s)
   if proto == nil:
     addInterfaceOverloadableSymAt(c, c.currentScope, s)
@@ -655,7 +658,7 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode =
     if s != nil and s.owner == c.owner and s.kind == skParam:
       result = newParam(c, n, s)
     else:
-      localError(n.info, errInvalidExpression)
+      localError(c.c.config, n.info, "invalid expression")
       result = n
 
   proc stupidStmtListExpr(n: PNode): bool =
@@ -675,7 +678,7 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode =
     # we support '(pattern){x}' to bind a subpattern to a parameter 'x';
     # '(pattern){|x}' does the same but the matches will be gathered in 'x'
     if n.len != 2:
-      localError(n.info, errInvalidExpression)
+      localError(c.c.config, n.info, "invalid expression")
     elif n.sons[1].kind == nkIdent:
       n.sons[0] = semPatternBody(c, n.sons[0])
       n.sons[1] = expectParam(c, n.sons[1])
@@ -685,9 +688,9 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode =
         n.sons[0] = semPatternBody(c, n.sons[0])
         n.sons[1].sons[1] = expectParam(c, n.sons[1].sons[1])
       else:
-        localError(n.info, errInvalidExpression)
+        localError(c.c.config, n.info, "invalid expression")
     else:
-      localError(n.info, errInvalidExpression)
+      localError(c.c.config, n.info, "invalid expression")
   of nkStmtList, nkStmtListExpr:
     if stupidStmtListExpr(n):
       result = semPatternBody(c, n.lastSon)
@@ -759,5 +762,5 @@ proc semPattern(c: PContext, n: PNode): PNode =
     if result.len == 1:
       result = result.sons[0]
     elif result.len == 0:
-      localError(n.info, errInvalidExpression)
+      localError(c.config, n.info, "a pattern cannot be empty")
   closeScope(c)
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index 1fc263617..8b5c26f99 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -10,6 +10,33 @@
 # this module does the semantic checking of type declarations
 # included from sem.nim
 
+const
+  errStringLiteralExpected = "string literal expected"
+  errIntLiteralExpected = "integer literal expected"
+  errWrongNumberOfVariables = "wrong number of variables"
+  errInvalidOrderInEnumX = "invalid order in enum '$1'"
+  errOrdinalTypeExpected = "ordinal type expected"
+  errSetTooBig = "set is too large"
+  errBaseTypeMustBeOrdinal = "base type of a set must be an ordinal"
+  errInheritanceOnlyWithNonFinalObjects = "inheritance only works with non-final objects"
+  errXExpectsOneTypeParam = "'$1' expects one type parameter"
+  errArrayExpectsTwoTypeParams = "array expects two type parameters"
+  errInvalidVisibilityX = "invalid visibility: '$1'"
+  errInitHereNotAllowed = "initialization not allowed here"
+  errXCannotBeAssignedTo = "'$1' cannot be assigned to"
+  errIteratorNotAllowed = "iterators can only be defined at the module's top level"
+  errXNeedsReturnType = "$1 needs a return type"
+  errNoReturnTypeDeclared = "no return type declared"
+  errTIsNotAConcreteType = "'$1' is not a concrete type"
+  errTypeExpected = "type expected"
+  errXOnlyAtModuleScope = "'$1' is only allowed at top level"
+  errDuplicateCaseLabel = "duplicate case label"
+  errMacroBodyDependsOnGenericTypes = "the macro body cannot be compiled, " &
+    "because the parameter '$1' has a generic type"
+  errIllegalRecursionInTypeX = "illegal recursion in type '$1'"
+  errNoGenericParamsAllowedForX = "no generic parameters allowed for $1"
+  errInOutFlagNotExtern = "the '$1' modifier can be used only with imported types"
+
 proc newOrPrevType(kind: TTypeKind, prev: PType, c: PContext): PType =
   if prev == nil:
     result = newTypeS(kind, c)
@@ -34,11 +61,11 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
   base = nil
   result = newOrPrevType(tyEnum, prev, c)
   result.n = newNodeI(nkEnumTy, n.info)
-  checkMinSonsLen(n, 1)
+  checkMinSonsLen(n, 1, c.config)
   if n.sons[0].kind != nkEmpty:
     base = semTypeNode(c, n.sons[0].sons[0], nil)
     if base.kind != tyEnum:
-      localError(n.sons[0].info, errInheritanceOnlyWithEnums)
+      localError(c.config, n.sons[0].info, "inheritance only works with an enum")
     counter = lastOrd(base) + 1
   rawAddSon(result, base)
   let isPure = result.sym != nil and sfPure in result.sym.flags
@@ -58,9 +85,9 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
           if skipTypes(strVal.typ, abstractInst).kind in {tyString, tyCString}:
             x = getOrdValue(v.sons[0]) # first tuple part is the ordinal
           else:
-            localError(strVal.info, errStringLiteralExpected)
+            localError(c.config, strVal.info, errStringLiteralExpected)
         else:
-          localError(v.info, errWrongNumberOfVariables)
+          localError(c.config, v.info, errWrongNumberOfVariables)
       of tyString, tyCString:
         strVal = v
         x = counter
@@ -69,7 +96,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
       if i != 1:
         if x != counter: incl(result.flags, tfEnumHasHoles)
         if x < counter:
-          localError(n.sons[i].info, errInvalidOrderInEnumX, e.name.s)
+          localError(c.config, n.sons[i].info, errInvalidOrderInEnumX % e.name.s)
           x = counter
       e.ast = strVal # might be nil
       counter = x
@@ -78,7 +105,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
     of nkIdent, nkAccQuoted:
       e = newSymS(skEnumField, n.sons[i], c)
     else:
-      illFormedAst(n[i])
+      illFormedAst(n[i], c.config)
     e.typ = result
     e.position = int(counter)
     if e.position == 0: hasNull = true
@@ -87,12 +114,13 @@ 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)
     if sfGenSym notin e.flags:
       if not isPure: addDecl(c, e)
       else: importPureEnumField(c, e)
     if isPure and strTableIncl(symbols, e):
-      wrongRedefinition(e.info, e.name.s)
+      wrongRedefinition(c, e.info, e.name.s)
     inc(counter)
   if not hasNull: incl(result.flags, tfNeedsInit)
 
@@ -104,11 +132,11 @@ proc semSet(c: PContext, n: PNode, prev: PType): PType =
     if base.kind in {tyGenericInst, tyAlias, tySink}: base = lastSon(base)
     if base.kind != tyGenericParam:
       if not isOrdinalType(base):
-        localError(n.info, errOrdinalTypeExpected)
+        localError(c.config, n.info, errOrdinalTypeExpected)
       elif lengthOrd(base) > MaxSetElements:
-        localError(n.info, errSetTooBig)
+        localError(c.config, n.info, errSetTooBig)
   else:
-    localError(n.info, errXExpectsOneTypeParam, "set")
+    localError(c.config, n.info, errXExpectsOneTypeParam % "set")
     addSonSkipIntLit(result, errorType(c))
 
 proc semContainer(c: PContext, n: PNode, kind: TTypeKind, kindStr: string,
@@ -117,10 +145,10 @@ proc semContainer(c: PContext, n: PNode, kind: TTypeKind, kindStr: string,
   if sonsLen(n) == 2:
     var base = semTypeNode(c, n.sons[1], nil)
     if base.kind == tyVoid:
-      localError(n.info, errTIsNotAConcreteType, typeToString(base))
+      localError(c.config, n.info, errTIsNotAConcreteType % typeToString(base))
     addSonSkipIntLit(result, base)
   else:
-    localError(n.info, errXExpectsOneTypeParam, kindStr)
+    localError(c.config, n.info, errXExpectsOneTypeParam % kindStr)
     addSonSkipIntLit(result, errorType(c))
 
 proc semVarargs(c: PContext, n: PNode, prev: PType): PType =
@@ -129,9 +157,9 @@ 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(n.sons[2]), n.sons[2].info)
+      result.n = newIdentNode(considerQuotedIdent(c.config, n.sons[2]), n.sons[2].info)
   else:
-    localError(n.info, errXExpectsOneTypeParam, "varargs")
+    localError(c.config, n.info, errXExpectsOneTypeParam % "varargs")
     addSonSkipIntLit(result, errorType(c))
 
 proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType =
@@ -140,7 +168,7 @@ proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType =
   else:
     let isCall = int ord(n.kind in nkCallKinds+{nkBracketExpr})
     let n = if n[0].kind == nkBracket: n[0] else: n
-    checkMinSonsLen(n, 1)
+    checkMinSonsLen(n, 1, c.config)
     var t = semTypeNode(c, n.lastSon, nil)
     if t.kind == tyTypeDesc and tfUnresolved notin t.flags:
       t = t.base
@@ -155,7 +183,7 @@ proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType =
         let region = semTypeNode(c, ni, nil)
         if region.skipTypes({tyGenericInst, tyAlias, tySink}).kind notin {
               tyError, tyObject}:
-          message n[i].info, errGenerated, "region needs to be an object type"
+          message c.config, n[i].info, errGenerated, "region needs to be an object type"
         addSonSkipIntLit(result, region)
     addSonSkipIntLit(result, t)
     if tfPartial in result.flags:
@@ -167,7 +195,7 @@ proc semVarType(c: PContext, n: PNode, prev: PType): PType =
     result = newOrPrevType(tyVar, prev, c)
     var base = semTypeNode(c, n.sons[0], nil).skipTypes({tyTypeDesc})
     if base.kind == tyVar:
-      localError(n.info, errVarVarTypeNotAllowed)
+      localError(c.config, n.info, "type 'var var' is not allowed")
       base = base.sons[0]
     addSonSkipIntLit(result, base)
   else:
@@ -181,7 +209,7 @@ proc semDistinct(c: PContext, n: PNode, prev: PType): PType =
 
 proc semRangeAux(c: PContext, n: PNode, prev: PType): PType =
   assert isRange(n)
-  checkSonsLen(n, 3)
+  checkSonsLen(n, 3, c.config)
   result = newOrPrevType(tyRange, prev, c)
   result.n = newNodeI(nkRange, n.info)
   # always create a 'valid' range type, but overwrite it later
@@ -189,7 +217,7 @@ proc semRangeAux(c: PContext, n: PNode, prev: PType): PType =
   addSonSkipIntLit(result, errorType(c))
 
   if (n[1].kind == nkEmpty) or (n[2].kind == nkEmpty):
-    localError(n.info, errRangeIsEmpty)
+    localError(c.config, n.info, "range is empty")
 
   var range: array[2, PNode]
   range[0] = semExprWithType(c, n[1], {efDetermineType})
@@ -204,11 +232,11 @@ proc semRangeAux(c: PContext, n: PNode, prev: PType): PType =
 
   if not hasUnknownTypes:
     if not sameType(rangeT[0].skipTypes({tyRange}), rangeT[1].skipTypes({tyRange})):
-      localError(n.info, errPureTypeMismatch)
+      localError(c.config, n.info, "type mismatch")
     elif not rangeT[0].isOrdinalType:
-      localError(n.info, errOrdinalTypeExpected)
+      localError(c.config, n.info, "ordinal type expected")
     elif enumHasHoles(rangeT[0]):
-      localError(n.info, errEnumXHasHoles, rangeT[0].sym.name.s)
+      localError(c.config, n.info, "enum '$1' has holes" % typeToString(rangeT[0]))
 
   for i in 0..1:
     if hasGenericArguments(range[i]):
@@ -218,7 +246,7 @@ proc semRangeAux(c: PContext, n: PNode, prev: PType): PType =
       result.n.addSon semConstExpr(c, range[i])
 
   if weakLeValue(result.n[0], result.n[1]) == impNo:
-    localError(n.info, errRangeIsEmpty)
+    localError(c.config, n.info, "range is empty")
 
   result[0] = rangeT[0]
 
@@ -239,13 +267,13 @@ 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(n[1][0]).s == "..<":
-        localError(n[0].info, "range types need to be constructed with '..', '..<' is not supported")
+      if n[1].kind == nkInfix and considerQuotedIdent(c.config, n[1][0]).s == "..<":
+        localError(c.config, n[0].info, "range types need to be constructed with '..', '..<' is not supported")
       else:
-        localError(n.sons[0].info, errRangeExpected)
+        localError(c.config, n.sons[0].info, "expected range")
       result = newOrPrevType(tyError, prev, c)
   else:
-    localError(n.info, errXExpectsOneTypeParam, "range")
+    localError(c.config, n.info, errXExpectsOneTypeParam % "range")
     result = newOrPrevType(tyError, prev, c)
 
 proc semArrayIndex(c: PContext, n: PNode): PType =
@@ -257,7 +285,7 @@ proc semArrayIndex(c: PContext, n: PNode): PType =
       result = makeRangeWithStaticExpr(c, e.typ.n)
     elif e.kind in {nkIntLit..nkUInt64Lit}:
       if e.intVal < 0:
-        localError(n[1].info,
+        localError(c.config, n[1].info,
           "Array length can't be negative, but was " & $e.intVal)
       result = makeRangeType(c, 0, e.intVal-1, n.info, e.typ)
     elif e.kind == nkSym and e.typ.kind == tyStatic:
@@ -265,12 +293,12 @@ proc semArrayIndex(c: PContext, n: PNode): PType =
         return semArrayIndex(c, e.sym.ast)
       if not isOrdinalType(e.typ.lastSon):
         let info = if n.safeLen > 1: n[1].info else: n.info
-        localError(info, errOrdinalTypeExpected)
+        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):
       if not isOrdinalType(e.typ):
-        localError(n[1].info, errOrdinalTypeExpected)
+        localError(c.config, n[1].info, errOrdinalTypeExpected)
       # This is an int returning call, depending on an
       # yet unknown generic param (see tgenericshardcases).
       # We are going to construct a range type that will be
@@ -286,7 +314,7 @@ proc semArrayIndex(c: PContext, n: PNode): PType =
                              x.typ.skipTypes({tyTypeDesc}))
       else:
         result = x.typ.skipTypes({tyTypeDesc})
-        #localError(n[1].info, errConstExprExpected)
+        #localError(c.config, n[1].info, errConstExprExpected)
 
 proc semArray(c: PContext, n: PNode, prev: PType): PType =
   var base: PType
@@ -299,9 +327,9 @@ proc semArray(c: PContext, n: PNode, prev: PType): PType =
       if indxB.skipTypes({tyRange}).kind in {tyUInt, tyUInt64}:
         discard
       elif not isOrdinalType(indxB):
-        localError(n.sons[1].info, errOrdinalTypeExpected)
+        localError(c.config, n.sons[1].info, errOrdinalTypeExpected)
       elif enumHasHoles(indxB):
-        localError(n.sons[1].info, errEnumXHasHoles,
+        localError(c.config, n.sons[1].info, "enum '$1' has holes" %
                    typeToString(indxB.skipTypes({tyRange})))
     base = semTypeNode(c, n.sons[2], nil)
     # ensure we only construct a tyArray when there was no error (bug #3048):
@@ -311,7 +339,7 @@ proc semArray(c: PContext, n: PNode, prev: PType): PType =
     rawAddSonNoPropagationOfTypeFlags(result, indx)
     addSonSkipIntLit(result, base)
   else:
-    localError(n.info, errArrayExpectsTwoTypeParams)
+    localError(c.config, n.info, errArrayExpectsTwoTypeParams)
     result = newOrPrevType(tyError, prev, c)
 
 proc semOrdinal(c: PContext, n: PNode, prev: PType): PType =
@@ -320,10 +348,10 @@ proc semOrdinal(c: PContext, n: PNode, prev: PType): PType =
     var base = semTypeNode(c, n.sons[1], nil)
     if base.kind != tyGenericParam:
       if not isOrdinalType(base):
-        localError(n.sons[1].info, errOrdinalTypeExpected)
+        localError(c.config, n.sons[1].info, errOrdinalTypeExpected)
     addSonSkipIntLit(result, base)
   else:
-    localError(n.info, errXExpectsOneTypeParam, "ordinal")
+    localError(c.config, n.info, errXExpectsOneTypeParam % "ordinal")
     result = newOrPrevType(tyError, prev, c)
 
 proc semTypeIdent(c: PContext, n: PNode): PSym =
@@ -334,7 +362,7 @@ proc semTypeIdent(c: PContext, n: PNode): PSym =
     if result.isNil:
       result = qualifiedLookUp(c, n, {checkAmbiguity, checkUndeclared})
     if result != nil:
-      markUsed(n.info, result, c.graph.usageSym)
+      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?
@@ -345,7 +373,7 @@ proc semTypeIdent(c: PContext, n: PNode): PSym =
           if bound != nil: return bound
           return result
         if result.typ.sym == nil:
-          localError(n.info, errTypeExpected)
+          localError(c.config, n.info, errTypeExpected)
           return errorSym(c, n)
         result = result.typ.sym.copySym
         result.typ = copyType(result.typ, result.typ.owner, true)
@@ -359,7 +387,7 @@ proc semTypeIdent(c: PContext, n: PNode): PSym =
           result.typ.flags.excl tfWildcard
           return
         else:
-          localError(n.info, errTypeExpected)
+          localError(c.config, n.info, errTypeExpected)
           return errorSym(c, n)
 
       if result.kind != skType:
@@ -370,7 +398,7 @@ proc semTypeIdent(c: PContext, n: PNode): PSym =
           amb = nextOverloadIter(ov, c, n)
         if amb != nil: result = amb
         else:
-          if result.kind != skError: localError(n.info, errTypeExpected)
+          if result.kind != skError: localError(c.config, n.info, errTypeExpected)
           return errorSym(c, n)
       if result.typ.kind != tyGenericParam:
         # XXX get rid of this hack!
@@ -385,12 +413,12 @@ proc semTypeIdent(c: PContext, n: PNode): PSym =
         n.info = oldInfo
         n.typ = result.typ
     else:
-      localError(n.info, errIdentifierExpected)
+      localError(c.config, n.info, "identifier expected")
       result = errorSym(c, n)
 
 proc semAnonTuple(c: PContext, n: PNode, prev: PType): PType =
   if sonsLen(n) == 0:
-    localError(n.info, errTypeExpected)
+    localError(c.config, n.info, errTypeExpected)
   result = newOrPrevType(tyTuple, prev, c)
   for it in n:
     addSonSkipIntLit(result, semTypeNode(c, it, nil))
@@ -403,27 +431,27 @@ proc semTuple(c: PContext, n: PNode, prev: PType): PType =
   var counter = 0
   for i in countup(ord(n.kind == nkBracketExpr), sonsLen(n) - 1):
     var a = n.sons[i]
-    if (a.kind != nkIdentDefs): illFormedAst(a)
-    checkMinSonsLen(a, 3)
+    if (a.kind != nkIdentDefs): illFormedAst(a, c.config)
+    checkMinSonsLen(a, 3, c.config)
     var length = sonsLen(a)
     if a.sons[length - 2].kind != nkEmpty:
       typ = semTypeNode(c, a.sons[length - 2], nil)
     else:
-      localError(a.info, errTypeExpected)
+      localError(c.config, a.info, errTypeExpected)
       typ = errorType(c)
     if a.sons[length - 1].kind != nkEmpty:
-      localError(a.sons[length - 1].info, errInitHereNotAllowed)
+      localError(c.config, a.sons[length - 1].info, errInitHereNotAllowed)
     for j in countup(0, length - 3):
       var field = newSymG(skField, a.sons[j], c)
       field.typ = typ
       field.position = counter
       inc(counter)
       if containsOrIncl(check, field.name.id):
-        localError(a.sons[j].info, errAttemptToRedefine, field.name.s)
+        localError(c.config, a.sons[j].info, "attempt to redefine: '" & field.name.s & "'")
       else:
         addSon(result.n, newSymNode(field))
         addSonSkipIntLit(result, typ)
-      if gCmd == cmdPretty: styleCheckDef(a.sons[j].info, field)
+      if c.config.cmd == cmdPretty: styleCheckDef(a.sons[j].info, field)
   if result.n.len == 0: result.n = nil
 
 proc semIdentVis(c: PContext, kind: TSymKind, n: PNode,
@@ -434,23 +462,23 @@ 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(n.sons[0])
+      var v = considerQuotedIdent(c.config, n.sons[0])
       if sfExported in allowed and v.id == ord(wStar):
         incl(result.flags, sfExported)
       else:
         if not (sfExported in allowed):
-          localError(n.sons[0].info, errXOnlyAtModuleScope, "export")
+          localError(c.config, n.sons[0].info, errXOnlyAtModuleScope % "export")
         else:
-          localError(n.sons[0].info, errInvalidVisibilityX, renderTree(n[0]))
+          localError(c.config, n.sons[0].info, errInvalidVisibilityX % renderTree(n[0]))
     else:
-      illFormedAst(n)
+      illFormedAst(n, c.config)
   else:
     result = newSymG(kind, n, c)
 
 proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode,
                         allowed: TSymFlags): PSym =
   if n.kind == nkPragmaExpr:
-    checkSonsLen(n, 2)
+    checkSonsLen(n, 2, c.config)
     result = semIdentVis(c, kind, n.sons[0], allowed)
     case kind
     of skType:
@@ -463,7 +491,7 @@ proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode,
     else: discard
   else:
     result = semIdentVis(c, kind, n, allowed)
-  if gCmd == cmdPretty: styleCheckDef(n.info, result)
+  if c.config.cmd == cmdPretty: styleCheckDef(n.info, result)
 
 proc checkForOverlap(c: PContext, t: PNode, currentEx, branchIndex: int) =
   let ex = t[branchIndex][currentEx].skipConv
@@ -471,10 +499,10 @@ proc checkForOverlap(c: PContext, t: PNode, currentEx, branchIndex: int) =
     for j in countup(0, sonsLen(t.sons[i]) - 2):
       if i == branchIndex and j == currentEx: break
       if overlap(t.sons[i].sons[j].skipConv, ex):
-        localError(ex.info, errDuplicateCaseLabel)
+        localError(c.config, ex.info, errDuplicateCaseLabel)
 
 proc semBranchRange(c: PContext, t, a, b: PNode, covered: var BiggestInt): PNode =
-  checkMinSonsLen(t, 1)
+  checkMinSonsLen(t, 1, c.config)
   let ac = semConstExpr(c, a)
   let bc = semConstExpr(c, b)
   let at = fitNode(c, t.sons[0].typ, ac, ac.info).skipConvTakeType
@@ -483,21 +511,21 @@ proc semBranchRange(c: PContext, t, a, b: PNode, covered: var BiggestInt): PNode
   result = newNodeI(nkRange, a.info)
   result.add(at)
   result.add(bt)
-  if emptyRange(ac, bc): localError(b.info, errRangeIsEmpty)
+  if emptyRange(ac, bc): localError(c.config, b.info, "range is empty")
   else: covered = covered + getOrdValue(bc) - getOrdValue(ac) + 1
 
 proc semCaseBranchRange(c: PContext, t, b: PNode,
                         covered: var BiggestInt): PNode =
-  checkSonsLen(b, 3)
+  checkSonsLen(b, 3, c.config)
   result = semBranchRange(c, t, b.sons[1], b.sons[2], covered)
 
 proc semCaseBranchSetElem(c: PContext, t, b: PNode,
                           covered: var BiggestInt): PNode =
   if isRange(b):
-    checkSonsLen(b, 3)
+    checkSonsLen(b, 3, c.config)
     result = semBranchRange(c, t, b.sons[1], b.sons[2], covered)
   elif b.kind == nkRange:
-    checkSonsLen(b, 2)
+    checkSonsLen(b, 2, c.config)
     result = semBranchRange(c, t, b.sons[0], b.sons[1], covered)
   else:
     result = fitNode(c, t.sons[0].typ, b, b.info)
@@ -520,7 +548,7 @@ proc semCaseBranch(c: PContext, t, branch: PNode, branchIndex: int,
         delSon(branch, 0)
         return
       elif r.kind notin {nkCurly, nkBracket} or len(r) == 0:
-        checkMinSonsLen(t, 1)
+        checkMinSonsLen(t, 1, c.config)
         branch.sons[i] = skipConv(fitNode(c, t.sons[0].typ, r, r.info))
         inc(covered)
       else:
@@ -546,37 +574,37 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
 proc semRecordCase(c: PContext, n: PNode, check: var IntSet, pos: var int,
                    father: PNode, rectype: PType) =
   var a = copyNode(n)
-  checkMinSonsLen(n, 2)
+  checkMinSonsLen(n, 2, c.config)
   semRecordNodeAux(c, n.sons[0], check, pos, a, rectype)
   if a.sons[0].kind != nkSym:
-    internalError("semRecordCase: discriminant is no symbol")
+    internalError(c.config, "semRecordCase: discriminant is no symbol")
     return
   incl(a.sons[0].sym.flags, sfDiscriminant)
   var covered: BiggestInt = 0
   var typ = skipTypes(a.sons[0].typ, abstractVar-{tyTypeDesc})
   if not isOrdinalType(typ):
-    localError(n.info, errSelectorMustBeOrdinal)
+    localError(c.config, n.info, "selector must be of an ordinal type")
   elif firstOrd(typ) != 0:
-    localError(n.info, errGenerated, "low(" & $a.sons[0].sym.name.s &
+    localError(c.config, n.info, "low(" & $a.sons[0].sym.name.s &
                                      ") must be 0 for discriminant")
   elif lengthOrd(typ) > 0x00007FFF:
-    localError(n.info, errLenXinvalid, a.sons[0].sym.name.s)
+    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):
     var b = copyTree(n.sons[i])
     addSon(a, b)
     case n.sons[i].kind
     of nkOfBranch:
-      checkMinSonsLen(b, 2)
+      checkMinSonsLen(b, 2, c.config)
       semCaseBranch(c, a, b, i, covered)
     of nkElse:
       chckCovered = false
-      checkSonsLen(b, 1)
-    else: illFormedAst(n)
+      checkSonsLen(b, 1, c.config)
+    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)):
-    localError(a.info, errNotAllCasesCovered)
+  if chckCovered and covered != lengthOrd(a.sons[0].typ):
+    localError(c.config, a.info, "not all cases are covered")
   addSon(father, a)
 
 proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
@@ -587,22 +615,22 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
     var branch: PNode = nil   # the branch to take
     for i in countup(0, sonsLen(n) - 1):
       var it = n.sons[i]
-      if it == nil: illFormedAst(n)
+      if it == nil: illFormedAst(n, c.config)
       var idx = 1
       case it.kind
       of nkElifBranch:
-        checkSonsLen(it, 2)
+        checkSonsLen(it, 2, c.config)
         if c.inGenericContext == 0:
           var e = semConstBoolExpr(c, it.sons[0])
-          if e.kind != nkIntLit: internalError(e.info, "semRecordNodeAux")
+          if e.kind != nkIntLit: internalError(c.config, e.info, "semRecordNodeAux")
           elif e.intVal != 0 and branch == nil: branch = it.sons[1]
         else:
           it.sons[0] = forceBool(c, semExprWithType(c, it.sons[0]))
       of nkElse:
-        checkSonsLen(it, 1)
+        checkSonsLen(it, 1, c.config)
         if branch == nil: branch = it.sons[0]
         idx = 0
-      else: illFormedAst(n)
+      else: illFormedAst(n, c.config)
       if c.inGenericContext > 0:
         # use a new check intset here for each branch:
         var newCheck: IntSet
@@ -626,16 +654,16 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
       semRecordNodeAux(c, n.sons[i], check, pos, a, rectype)
     if a != father: addSon(father, a)
   of nkIdentDefs:
-    checkMinSonsLen(n, 3)
+    checkMinSonsLen(n, 3, c.config)
     var length = sonsLen(n)
     var a: PNode
     if father.kind != nkRecList and length>=4: a = newNodeI(nkRecList, n.info)
     else: a = ast.emptyNode
     if n.sons[length-1].kind != nkEmpty:
-      localError(n.sons[length-1].info, errInitHereNotAllowed)
+      localError(c.config, n.sons[length-1].info, errInitHereNotAllowed)
     var typ: PType
     if n.sons[length-2].kind == nkEmpty:
-      localError(n.info, errTypeExpected)
+      localError(c.config, n.info, errTypeExpected)
       typ = errorType(c)
     else:
       typ = semTypeNode(c, n.sons[length-2], nil)
@@ -644,7 +672,7 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
                      else: rectype.sym
     for i in countup(0, sonsLen(n)-3):
       var f = semIdentWithPragma(c, skField, n.sons[i], {sfExported})
-      suggestSym(n.sons[i].info, f, c.graph.usageSym)
+      suggestSym(c.config, n.sons[i].info, f, c.graph.usageSym)
       f.typ = typ
       f.position = pos
       if fieldOwner != nil and
@@ -654,7 +682,7 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
         f.flags = f.flags + ({sfImportc, sfExportc} * fieldOwner.flags)
       inc(pos)
       if containsOrIncl(check, f.name.id):
-        localError(n.sons[i].info, errAttemptToRedefine, f.name.s)
+        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)
@@ -664,29 +692,29 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
     # inherited from generic/partial specialized parent second check.
     # There is no branch validity check here
     if containsOrIncl(check, n.sym.name.id):
-      localError(n.info, errAttemptToRedefine, n.sym.name.s)
+      localError(c.config, n.info, "attempt to redefine: '" & n.sym.name.s & "'")
     addSon(father, n)
   of nkEmpty: discard
-  else: illFormedAst(n)
+  else: illFormedAst(n, c.config)
 
 proc addInheritedFieldsAux(c: PContext, check: var IntSet, pos: var int,
                            n: PNode) =
   case n.kind
   of nkRecCase:
-    if (n.sons[0].kind != nkSym): internalError(n.info, "addInheritedFieldsAux")
+    if (n.sons[0].kind != nkSym): internalError(c.config, n.info, "addInheritedFieldsAux")
     addInheritedFieldsAux(c, check, pos, n.sons[0])
     for i in countup(1, sonsLen(n) - 1):
       case n.sons[i].kind
       of nkOfBranch, nkElse:
         addInheritedFieldsAux(c, check, pos, lastSon(n.sons[i]))
-      else: internalError(n.info, "addInheritedFieldsAux(record case branch)")
+      else: internalError(c.config, n.info, "addInheritedFieldsAux(record case branch)")
   of nkRecList:
     for i in countup(0, sonsLen(n) - 1):
       addInheritedFieldsAux(c, check, pos, n.sons[i])
   of nkSym:
     incl(check, n.sym.name.id)
     inc(pos)
-  else: internalError(n.info, "addInheritedFieldsAux()")
+  else: internalError(c.config, n.info, "addInheritedFieldsAux()")
 
 proc skipGenericInvocation(t: PType): PType {.inline.} =
   result = t
@@ -709,12 +737,12 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType): PType =
   var pos = 0
   var base, realBase: PType = nil
   # n.sons[0] contains the pragmas (if any). We process these later...
-  checkSonsLen(n, 3)
+  checkSonsLen(n, 3, c.config)
   if n.sons[1].kind != nkEmpty:
     realBase = semTypeNode(c, n.sons[1].sons[0], nil)
     base = skipTypesOrNil(realBase, skipPtrs)
     if base.isNil:
-      localError(n.info, errIllegalRecursionInTypeX, "object")
+      localError(c.config, n.info, "cannot inherit from a type that is not an object type")
     else:
       var concreteBase = skipGenericInvocation(base)
       if concreteBase.kind in {tyObject, tyGenericParam,
@@ -727,11 +755,11 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType): PType =
           addInheritedFields(c, check, pos, concreteBase)
       else:
         if concreteBase.kind != tyError:
-          localError(n.sons[1].info, "inheritance only works with non-final objects; " &
+          localError(c.config, n.sons[1].info, "inheritance only works with non-final objects; " &
              "to enable inheritance write '" & typeToString(realBase) & " of RootObj'")
         base = nil
         realBase = nil
-  if n.kind != nkObjectTy: internalError(n.info, "semObjectNode")
+  if n.kind != nkObjectTy: internalError(c.config, n.info, "semObjectNode")
   result = newOrPrevType(tyObject, prev, c)
   rawAddSon(result, realBase)
   if result.n.isNil:
@@ -769,8 +797,7 @@ proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) =
       addDecl(c, param)
     else:
       # within a macro, every param has the type NimNode!
-      let nn = if getCompilerProc("NimNode") != nil: getSysSym"NimNode"
-               else: getSysSym"PNimrodNode"
+      let nn = getSysSym(c.graph, param.info, "NimNode")
       var a = copySym(param)
       a.typ = nn.typ
       addDecl(c, a)
@@ -780,7 +807,7 @@ proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) =
 let typedescId = getIdent"typedesc"
 
 template shouldHaveMeta(t) =
-  internalAssert tfHasMeta in t.flags
+  internalAssert c.config, tfHasMeta in t.flags
   # result.lastSon.flags.incl tfHasMeta
 
 proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
@@ -838,7 +865,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
     if tfUnresolved in paramType.flags: return # already lifted
     let base = paramType.base.maybeLift
     if base.isMetaType and procKind == skMacro:
-      localError(info, errMacroBodyDependsOnGenericTypes, paramName)
+      localError(c.config, info, errMacroBodyDependsOnGenericTypes % paramName)
     result = addImplicitGeneric(c.newTypeWithSons(tyStatic, @[base]))
     result.flags.incl({tfHasStatic, tfUnresolved})
 
@@ -870,7 +897,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
     else:
       for i in 0 ..< paramType.len:
         if paramType.sons[i] == paramType:
-          globalError(info, errIllegalRecursionInTypeX, typeToString(paramType))
+          globalError(c.config, info, errIllegalRecursionInTypeX % typeToString(paramType))
         var lifted = liftingWalk(paramType.sons[i])
         if lifted != nil:
           paramType.sons[i] = lifted
@@ -939,7 +966,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
     result = addImplicitGeneric(copyType(paramType, getCurrOwner(c), false))
 
   of tyGenericParam:
-    markUsed(info, paramType.sym, c.graph.usageSym)
+    markUsed(c.config, info, paramType.sym, c.graph.usageSym)
     styleCheckUse(info, paramType.sym)
     if tfWildcard in paramType.flags:
       paramType.flags.excl tfWildcard
@@ -952,7 +979,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
 proc semParamType(c: PContext, n: PNode, constraint: var PNode): PType =
   if n.kind == nkCurlyExpr:
     result = semTypeNode(c, n.sons[0], nil)
-    constraint = semNodeKindConstraints(n)
+    constraint = semNodeKindConstraints(n, c.config)
   else:
     result = semTypeNode(c, n, nil)
 
@@ -971,7 +998,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
   # for historical reasons (code grows) this is invoked for parameter
   # lists too and then 'isType' is false.
   var cl: IntSet
-  checkMinSonsLen(n, 1)
+  checkMinSonsLen(n, 1, c.config)
   result = newProcType(c, n.info, prev)
   if genericParams != nil and sonsLen(genericParams) == 0:
     cl = initIntSet()
@@ -985,8 +1012,8 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
       # skip this parameter here. It'll then be re-generated in another LL
       # pass over this instantiation:
       if a.kind == nkSym and sfFromGeneric in a.sym.flags: continue
-      illFormedAst(a)
-    checkMinSonsLen(a, 3)
+      illFormedAst(a, c.config)
+    checkMinSonsLen(a, 3, c.config)
     var
       typ: PType = nil
       def: PNode = nil
@@ -1009,7 +1036,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
         if not containsGenericType(typ):
           def = fitNode(c, typ, def, def.info)
     if not hasType and not hasDefault:
-      if isType: localError(a.info, "':' expected")
+      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:
@@ -1020,7 +1047,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
         let param = strTableGet(c.signatures, arg.name)
         if param != nil: typ = param.typ
         else:
-          localError(a.info, "typeless parameters are obsolete")
+          localError(c.config, a.info, "typeless parameters are obsolete")
           typ = errorType(c)
       let lifted = liftParamType(c, kind, genericParams, typ,
                                  arg.name.s, arg.info)
@@ -1031,11 +1058,11 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
       inc(counter)
       if def != nil and def.kind != nkEmpty: arg.ast = copyTree(def)
       if containsOrIncl(check, arg.name.id):
-        localError(a.sons[j].info, errAttemptToRedefine, arg.name.s)
+        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 gCmd == cmdPretty: styleCheckDef(a.sons[j].info, arg)
+      if c.config.cmd == cmdPretty: styleCheckDef(a.sons[j].info, arg)
 
   var r: PType
   if n.sons[0].kind != nkEmpty:
@@ -1085,7 +1112,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
         n.sym.typ.flags.excl tfWildcard
 
 proc semStmtListType(c: PContext, n: PNode, prev: PType): PType =
-  checkMinSonsLen(n, 1)
+  checkMinSonsLen(n, 1, c.config)
   var length = sonsLen(n)
   for i in countup(0, length - 2):
     n.sons[i] = semStmt(c, n.sons[i])
@@ -1098,7 +1125,7 @@ proc semStmtListType(c: PContext, n: PNode, prev: PType): PType =
 
 proc semBlockType(c: PContext, n: PNode, prev: PType): PType =
   inc(c.p.nestedBlockCounter)
-  checkSonsLen(n, 2)
+  checkSonsLen(n, 2, c.config)
   openScope(c)
   if n.sons[0].kind notin {nkEmpty, nkSym}:
     addDecl(c, newSymS(skLabel, n.sons[0], c))
@@ -1120,20 +1147,20 @@ proc semObjectTypeForInheritedGenericInst(c: PContext, n: PNode, t: PType) =
     realBase = t.sons[0]
     base = skipTypesOrNil(realBase, skipPtrs)
   if base.isNil:
-    localError(n.info, errIllegalRecursionInTypeX, "object")
+    localError(c.config, n.info, errIllegalRecursionInTypeX % "object")
   else:
     let concreteBase = skipGenericInvocation(base)
     if concreteBase.kind == tyObject and tfFinal notin concreteBase.flags:
       addInheritedFields(c, check, pos, concreteBase)
     else:
       if concreteBase.kind != tyError:
-        localError(n.info, errInheritanceOnlyWithNonFinalObjects)
+        localError(c.config, n.info, errInheritanceOnlyWithNonFinalObjects)
   var newf = newNodeI(nkRecList, n.info)
   semRecordNodeAux(c, t.n, check, pos, newf, t)
 
 proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
   if s.typ == nil:
-    localError(n.info, "cannot instantiate the '$1' $2" %
+    localError(c.config, n.info, "cannot instantiate the '$1' $2" %
                        [s.name.s, ($s.kind).substr(2).toLowerAscii])
     return newOrPrevType(tyError, prev, c)
 
@@ -1146,7 +1173,7 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
 
   template addToResult(typ) =
     if typ.isNil:
-      internalAssert false
+      internalAssert c.config, false
       rawAddSon(result, typ)
     else: addSonSkipIntLit(result, typ)
 
@@ -1158,7 +1185,7 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
   elif t.kind != tyGenericBody:
     # we likely got code of the form TypeA[TypeB] where TypeA is
     # not generic.
-    localError(n.info, errNoGenericParamsAllowedForX, s.name.s)
+    localError(c.config, n.info, errNoGenericParamsAllowedForX % s.name.s)
     return newOrPrevType(tyError, prev, c)
   else:
     var m = newCandidate(c, t)
@@ -1169,7 +1196,7 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
       let err = "cannot instantiate " & typeToString(t) & "\n" &
                 "got: <" & describeArgs(c, n) & ">\n" &
                 "but expected: <" & describeArgs(c, t.n, 0) & ">"
-      localError(n.info, errGenerated, err)
+      localError(c.config, n.info, errGenerated, err)
       return newOrPrevType(tyError, prev, c)
 
     var isConcrete = true
@@ -1187,7 +1214,7 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
     if isConcrete:
       if s.ast == nil and s.typ.kind != tyCompositeTypeClass:
         # XXX: What kind of error is this? is it still relevant?
-        localError(n.info, errCannotInstantiateX, s.name.s)
+        localError(c.config, n.info, errCannotInstantiateX % s.name.s)
         result = newOrPrevType(tyError, prev, c)
       else:
         result = instGenericContainer(c, n.info, result,
@@ -1197,7 +1224,7 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
   # generic/partial specialized parent
   let tx = result.skipTypes(abstractPtrs, 50)
   if tx.isNil:
-    localError(n.info, "invalid recursion in type '$1'" % typeToString(result[0]))
+    localError(c.config, n.info, "invalid recursion in type '$1'" % typeToString(result[0]))
     return errorType(c)
   if tx != result and tx.kind == tyObject and tx.sons[0] != nil:
     semObjectTypeForInheritedGenericInst(c, n, tx)
@@ -1226,7 +1253,7 @@ proc semTypeExpr(c: PContext, n: PNode; prev: PType): PType =
         let alias = maybeAliasType(c, result, prev)
         if alias != nil: result = alias
   else:
-    localError(n.info, errTypeExpected, n.renderTree)
+    localError(c.config, n.info, "expected type, but got: " & n.renderTree)
     result = errorType(c)
 
 proc freshType(res, prev: PType): PType {.inline.} =
@@ -1277,7 +1304,7 @@ proc semTypeClass(c: PContext, n: PNode, prev: PType): PType =
       dummyName = param
       dummyType = candidateTypeSlot
 
-    internalAssert dummyName.kind == nkIdent
+    internalAssert c.config, dummyName.kind == nkIdent
     var dummyParam = newSym(if modifier == tyTypeDesc: skType else: skVar,
                             dummyName.ident, owner, param.info)
     dummyParam.typ = dummyType
@@ -1289,7 +1316,7 @@ proc semTypeClass(c: PContext, n: PNode, prev: PType): PType =
 
 proc semProcTypeWithScope(c: PContext, n: PNode,
                         prev: PType, kind: TSymKind): PType =
-  checkSonsLen(n, 2)
+  checkSonsLen(n, 2, c.config)
   openScope(c)
   result = semProcTypeNode(c, n.sons[0], nil, prev, kind, isType=true)
   # start with 'ccClosure', but of course pragmas can overwrite this:
@@ -1299,7 +1326,7 @@ proc semProcTypeWithScope(c: PContext, n: PNode,
   s.typ = result
   if n.sons[1].kind != nkEmpty and n.sons[1].len > 0:
     pragma(c, s, n.sons[1], procTypePragmas)
-    when useEffectSystem: setEffectsForProcType(result, n.sons[1])
+    when useEffectSystem: setEffectsForProcType(c.graph, result, n.sons[1])
   closeScope(c)
 
 proc maybeAliasType(c: PContext; typeExpr, prev: PType): PType =
@@ -1320,19 +1347,19 @@ proc symFromExpectedTypeNode(c: PContext, n: PNode): PSym =
   if n.kind == nkType:
     result = symFromType(n.typ, n.info)
   else:
-    localError(n.info, errTypeExpected)
+    localError(c.config, n.info, errTypeExpected)
     result = errorSym(c, n)
 
 proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
   result = nil
   inc c.inTypeContext
 
-  if gCmd == cmdIdeTools: suggestExpr(c, n)
+  if c.config.cmd == cmdIdeTools: suggestExpr(c, n)
   case n.kind
   of nkEmpty: discard
   of nkTypeOfExpr:
     # for ``type(countup(1,3))``, see ``tests/ttoseq``.
-    checkSonsLen(n, 1)
+    checkSonsLen(n, 1, c.config)
     let typExpr = semExprWithType(c, n.sons[0], {efInTypeof})
     fixupTypeOf(c, prev, typExpr)
     result = typExpr.typ
@@ -1362,21 +1389,21 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
           result = freshType(result, prev)
           result.flags.excl(tfNotNil)
       else:
-        localError(n.info, errGenerated, "invalid type")
+        localError(c.config, n.info, errGenerated, "invalid type")
     elif n[0].kind notin nkIdentKinds:
       result = semTypeExpr(c, n, prev)
     else:
-      let op = considerQuotedIdent(n.sons[0])
+      let op = considerQuotedIdent(c.config, n.sons[0])
       if op.id in {ord(wAnd), ord(wOr)} or op.s == "|":
-        checkSonsLen(n, 3)
+        checkSonsLen(n, 3, c.config)
         var
           t1 = semTypeNode(c, n.sons[1], nil)
           t2 = semTypeNode(c, n.sons[2], nil)
         if t1 == nil:
-          localError(n.sons[1].info, errTypeExpected)
+          localError(c.config, n.sons[1].info, errTypeExpected)
           result = newOrPrevType(tyError, prev, c)
         elif t2 == nil:
-          localError(n.sons[2].info, errTypeExpected)
+          localError(c.config, n.sons[2].info, errTypeExpected)
           result = newOrPrevType(tyError, prev, c)
         else:
           result = if op.id == ord(wAnd): makeAndType(c, t1, t2)
@@ -1389,19 +1416,21 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
               n.sons[2].kind == nkNilLit:
             result = freshType(result, prev)
             result.flags.incl(tfNotNil)
+            if notnil notin c.features:
+              localError(c.config, n.info, "enable the 'not nil' annotation with {.experimental: \"notnil\".}")
           else:
-            localError(n.info, errGenerated, "invalid type")
+            localError(c.config, n.info, errGenerated, "invalid type")
         of 2:
           let negated = semTypeNode(c, n.sons[1], prev)
           result = makeNotType(c, negated)
         else:
-          localError(n.info, errGenerated, "invalid type")
+          localError(c.config, n.info, errGenerated, "invalid type")
       elif op.id == ord(wPtr):
         result = semAnyRef(c, n, tyPtr, prev)
       elif op.id == ord(wRef):
         result = semAnyRef(c, n, tyRef, prev)
       elif op.id == ord(wType):
-        checkSonsLen(n, 2)
+        checkSonsLen(n, 2, c.config)
         let typExpr = semExprWithType(c, n.sons[1], {efInTypeof})
         fixupTypeOf(c, prev, typExpr)
         result = typExpr.typ
@@ -1415,7 +1444,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
     if whenResult.kind == nkStmtList: whenResult.kind = nkStmtListType
     result = semTypeNode(c, whenResult, prev)
   of nkBracketExpr:
-    checkMinSonsLen(n, 2)
+    checkMinSonsLen(n, 2, c.config)
     var head = n.sons[0]
     var s = if head.kind notin nkCallKinds: semTypeIdent(c, head)
             else: symFromExpectedTypeNode(c, semExpr(c, head))
@@ -1442,7 +1471,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
       result = newOrPrevType(tyVar, prev, c)
       var base = semTypeNode(c, n.sons[1], nil)
       if base.kind in {tyVar, tyLent}:
-        localError(n.info, errVarVarTypeNotAllowed)
+        localError(c.config, n.info, "type 'var var' is not allowed")
         base = base.sons[0]
       addSonSkipIntLit(result, base)
     of mRef: result = semAnyRef(c, n, tyRef, prev)
@@ -1452,13 +1481,13 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
   of nkDotExpr:
     let typeExpr = semExpr(c, n)
     if typeExpr.typ.isNil:
-      localError(n.info, "object constructor needs an object type;" &
+      localError(c.config, n.info, "object constructor needs an object type;" &
           " for named arguments use '=' instead of ':'")
       result = errorType(c)
     elif typeExpr.typ.kind == tyFromExpr:
       result = typeExpr.typ
     elif typeExpr.typ.kind != tyTypeDesc:
-      localError(n.info, errTypeExpected)
+      localError(c.config, n.info, errTypeExpected)
       result = errorType(c)
     else:
       result = typeExpr.typ.base
@@ -1474,10 +1503,10 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
   of nkIdent, nkAccQuoted:
     var s = semTypeIdent(c, n)
     if s.typ == nil:
-      if s.kind != skError: localError(n.info, errTypeExpected)
+      if s.kind != skError: localError(c.config, n.info, errTypeExpected)
       result = newOrPrevType(tyError, prev, c)
     elif s.kind == skParam and s.typ.kind == tyTypeDesc:
-      internalAssert s.typ.base.kind != tyNone and prev == nil
+      internalAssert c.config, s.typ.base.kind != tyNone and prev == nil
       result = s.typ.base
     elif prev == nil:
       result = s.typ
@@ -1504,10 +1533,10 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
       else:
         assignType(prev, t)
         result = prev
-      markUsed(n.info, n.sym, c.graph.usageSym)
+      markUsed(c.config, n.info, n.sym, c.graph.usageSym)
       styleCheckUse(n.info, n.sym)
     else:
-      if s.kind != skError: localError(n.info, errTypeExpected)
+      if s.kind != skError: localError(c.config, n.info, errTypeExpected)
       result = newOrPrevType(tyError, prev, c)
   of nkObjectTy: result = semObjectNode(c, n, prev)
   of nkTupleTy: result = semTuple(c, n, prev)
@@ -1545,7 +1574,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
   of nkStmtListType: result = semStmtListType(c, n, prev)
   of nkBlockType: result = semBlockType(c, n, prev)
   else:
-    localError(n.info, errTypeExpected)
+    localError(c.config, n.info, errTypeExpected)
     result = newOrPrevType(tyError, prev, c)
   n.typ = result
   dec c.inTypeContext
@@ -1598,10 +1627,10 @@ proc processMagicType(c: PContext, m: PSym) =
   of mChar: setMagicType(m, tyChar, 1)
   of mString:
     setMagicType(m, tyString, ptrSize)
-    rawAddSon(m.typ, getSysType(tyChar))
+    rawAddSon(m.typ, getSysType(c.graph, m.info, tyChar))
   of mCstring:
     setMagicType(m, tyCString, ptrSize)
-    rawAddSon(m.typ, getSysType(tyChar))
+    rawAddSon(m.typ, getSysType(c.graph, m.info, tyChar))
   of mPointer: setMagicType(m, tyPointer, ptrSize)
   of mEmptySet:
     setMagicType(m, tySet, 1)
@@ -1647,8 +1676,8 @@ proc processMagicType(c: PContext, m: PSym) =
     case m.name.s
     of "lent": setMagicType(m, tyLent, ptrSize)
     of "sink": setMagicType(m, tySink, 0)
-    else: localError(m.info, errTypeExpected)
-  else: localError(m.info, errTypeExpected)
+    else: localError(c.config, m.info, errTypeExpected)
+  else: localError(c.config, m.info, errTypeExpected)
 
 proc semGenericConstraints(c: PContext, x: PType): PType =
   result = newTypeWithSons(c, tyGenericParam, @[x])
@@ -1656,11 +1685,11 @@ proc semGenericConstraints(c: PContext, x: PType): PType =
 proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode =
   result = copyNode(n)
   if n.kind != nkGenericParams:
-    illFormedAst(n)
+    illFormedAst(n, c.config)
     return
   for i in countup(0, sonsLen(n)-1):
     var a = n.sons[i]
-    if a.kind != nkIdentDefs: illFormedAst(n)
+    if a.kind != nkIdentDefs: illFormedAst(n, c.config)
     let L = a.len
     var def = a[^1]
     let constraint = a[^2]
@@ -1706,7 +1735,7 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode =
       if paramName.safeLen == 2:
         if not nimEnableCovariance or paramName[0].ident.s == "in":
           if father == nil or sfImportc notin father.sym.flags:
-            localError(paramName.info, errInOutFlagNotExtern, paramName[0].ident.s)
+            localError(c.config, paramName.info, errInOutFlagNotExtern % $paramName[0])
         covarianceFlag = if paramName[0].ident.s == "in": tfContravariant
                          else: tfCovariant
         if father != nil: father.flags.incl tfCovariant
diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim
index 09434c925..61d92bb19 100644
--- a/compiler/semtypinst.nim
+++ b/compiler/semtypinst.nim
@@ -14,21 +14,21 @@ import ast, astalgo, msgs, types, magicsys, semdata, renderer, options
 const
   tfInstClearedFlags = {tfHasMeta, tfUnresolved}
 
-proc checkPartialConstructedType(info: TLineInfo, t: PType) =
+proc checkPartialConstructedType(conf: ConfigRef; info: TLineInfo, t: PType) =
   if tfAcyclic in t.flags and skipTypes(t, abstractInst).kind != tyObject:
-    localError(info, errInvalidPragmaX, "acyclic")
+    localError(conf, info, "invalid pragma: acyclic")
   elif t.kind in {tyVar, tyLent} and t.sons[0].kind in {tyVar, tyLent}:
-    localError(info, errVarVarTypeNotAllowed)
+    localError(conf, info, "type 'var var' is not allowed")
 
-proc checkConstructedType*(info: TLineInfo, typ: PType) =
+proc checkConstructedType*(conf: ConfigRef; info: TLineInfo, typ: PType) =
   var t = typ.skipTypes({tyDistinct})
   if t.kind in tyTypeClasses: discard
   elif tfAcyclic in t.flags and skipTypes(t, abstractInst).kind != tyObject:
-    localError(info, errInvalidPragmaX, "acyclic")
+    localError(conf, info, "invalid pragma: acyclic")
   elif t.kind in {tyVar, tyLent} and t.sons[0].kind in {tyVar, tyLent}:
-    localError(info, errVarVarTypeNotAllowed)
+    localError(conf, info, "type 'var var' is not allowed")
   elif computeSize(t) == szIllegalRecursion:
-    localError(info, errIllegalRecursionInTypeX, typeToString(t))
+    localError(conf, info,  "illegal recursion in type '" & typeToString(t) & "'")
   when false:
     if t.kind == tyObject and t.sons[0] != nil:
       if t.sons[0].kind != tyObject or tfFinal in t.sons[0].flags:
@@ -36,9 +36,8 @@ proc checkConstructedType*(info: TLineInfo, typ: PType) =
 
 proc searchInstTypes*(key: PType): PType =
   let genericTyp = key.sons[0]
-  internalAssert genericTyp.kind == tyGenericBody and
-                 key.sons[0] == genericTyp and
-                 genericTyp.sym != nil
+  if not (genericTyp.kind == tyGenericBody and
+      key.sons[0] == genericTyp and genericTyp.sym != nil): return
 
   if genericTyp.sym.typeInstCache == nil:
     return
@@ -195,19 +194,19 @@ proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode; start=0): PNode =
     var branch: PNode = nil              # the branch to take
     for i in countup(0, sonsLen(n) - 1):
       var it = n.sons[i]
-      if it == nil: illFormedAst(n)
+      if it == nil: illFormedAst(n, cl.c.config)
       case it.kind
       of nkElifBranch:
-        checkSonsLen(it, 2)
+        checkSonsLen(it, 2, cl.c.config)
         var cond = prepareNode(cl, it.sons[0])
         var e = cl.c.semConstExpr(cl.c, cond)
         if e.kind != nkIntLit:
-          internalError(e.info, "ReplaceTypeVarsN: when condition not a bool")
+          internalError(cl.c.config, e.info, "ReplaceTypeVarsN: when condition not a bool")
         if e.intVal != 0 and branch == nil: branch = it.sons[1]
       of nkElse:
-        checkSonsLen(it, 1)
+        checkSonsLen(it, 1, cl.c.config)
         if branch == nil: branch = it.sons[0]
-      else: illFormedAst(n)
+      else: illFormedAst(n, cl.c.config)
     if branch != nil:
       result = replaceTypeVarsN(cl, branch)
     else:
@@ -244,14 +243,14 @@ proc lookupTypeVar(cl: var TReplTypeVars, t: PType): PType =
   result = cl.typeMap.lookup(t)
   if result == nil:
     if cl.allowMetaTypes or tfRetType in t.flags: return
-    localError(t.sym.info, errCannotInstantiateX, typeToString(t))
+    localError(cl.c.config, t.sym.info, "cannot instantiate: '" & typeToString(t) & "'")
     result = errorType(cl.c)
     # In order to prevent endless recursions, we must remember
     # this bad lookup and replace it with errorType everywhere.
     # These code paths are only active in "nim check"
     cl.typeMap.put(t, result)
   elif result.kind == tyGenericParam and not cl.allowMetaTypes:
-    internalError(cl.info, "substitution with generic parameter")
+    internalError(cl.c.config, cl.info, "substitution with generic parameter")
 
 proc instCopyType*(cl: var TReplTypeVars, t: PType): PType =
   # XXX: relying on allowMetaTypes is a kludge
@@ -278,7 +277,7 @@ 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.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:
@@ -351,7 +350,7 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
   # handleGenericInvocation will handle the alias-to-alias-to-alias case
   if newbody.isGenericAlias: newbody = newbody.skipGenericAlias
   rawAddSon(result, newbody)
-  checkPartialConstructedType(cl.info, newbody)
+  checkPartialConstructedType(cl.c.config, cl.info, newbody)
   let dc = newbody.deepCopy
   if cl.allowMetaTypes == false:
     if dc != nil and sfFromGeneric notin newbody.deepCopy.flags:
@@ -368,7 +367,7 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
         assert newbody.kind in {tyRef, tyPtr}
         assert newbody.lastSon.typeInst == nil
         newbody.lastSon.typeInst = result
-    if newDestructors:
+    if destructor in cl.c.features:
       cl.c.typesWithOps.add((newbody, result))
     else:
       typeBound(cl.c, newbody, result, assignment, cl.info)
@@ -417,7 +416,7 @@ proc propagateFieldFlags(t: PType, n: PNode) =
   # The type must be fully instantiated!
   if n.isNil:
     return
-  internalAssert n.kind != nkRecWhen
+  #internalAssert n.kind != nkRecWhen
   case n.kind
   of nkSym:
     propagateToOwner(t, n.sym.typ)
@@ -454,7 +453,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
       result.kind = tyUserTypeClassInst
 
   of tyGenericBody:
-    localError(cl.info, errCannotInstantiateX, typeToString(t))
+    localError(cl.c.config, cl.info, "cannot instantiate: '" & typeToString(t) & "'")
     result = errorType(cl.c)
     #result = replaceTypeVarsT(cl, lastSon(t))
 
@@ -533,7 +532,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
       case result.kind
       of tyArray:
         let idx = result.sons[0]
-        internalAssert idx.kind != tyStatic
+        internalAssert cl.c.config, idx.kind != tyStatic
 
       of tyObject, tyTuple:
         propagateFieldFlags(result, result.n)
@@ -545,7 +544,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
       else: discard
 
 proc instAllTypeBoundOp*(c: PContext, info: TLineInfo) =
-  if not newDestructors: return
+  if destructor notin c.features: return
   var i = 0
   while i < c.typesWithOps.len:
     let (newty, oldty) = c.typesWithOps[i]
diff --git a/compiler/service.nim b/compiler/service.nim
index ac04b7860..f1a988ae5 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
+  extccomp, strutils, os, platform, parseopt, idents, configuration
 
 when useCaas:
   import net
@@ -26,7 +26,7 @@ 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) =
+proc processCmdLine*(pass: TCmdLinePass, cmd: string; config: ConfigRef) =
   var p = parseopt.initOptParser(cmd)
   var argsCount = 0
   while true:
@@ -36,23 +36,23 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string) =
     of cmdLongoption, cmdShortOption:
       if p.key == " ":
         p.key = "-"
-        if processArgument(pass, p, argsCount): break
+        if processArgument(pass, p, argsCount, config): break
       else:
-        processSwitch(pass, p)
+        processSwitch(pass, p, config)
     of cmdArgument:
-      if processArgument(pass, p, argsCount): break
+      if processArgument(pass, p, argsCount, config): break
   if pass == passCmd2:
-    if optRun notin gGlobalOptions and arguments != "" and options.command.normalize != "run":
-      rawMessage(errArgsNeedRunOption, [])
+    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.}) =
+proc serve*(cache: IdentCache; action: proc (cache: IdentCache){.nimcall.}; config: ConfigRef) =
   template execute(cmd) =
     curCaasCmd = cmd
-    processCmdLine(passCmd2, cmd)
+    processCmdLine(passCmd2, cmd, config)
     action(cache)
-    gErrorCounter = 0
+    config.errorCounter = 0
 
-  let typ = getConfigVar("server.type")
+  let typ = getConfigVar(config, "server.type")
   case typ
   of "stdin":
     while true:
@@ -65,9 +65,9 @@ proc serve*(cache: IdentCache; action: proc (cache: IdentCache){.nimcall.}) =
   of "tcp", "":
     when useCaas:
       var server = newSocket()
-      let p = getConfigVar("server.port")
+      let p = getConfigVar(config, "server.port")
       let port = if p.len > 0: parseInt(p).Port else: 6000.Port
-      server.bindAddr(port, getConfigVar("server.address"))
+      server.bindAddr(port, getConfigVar(config, "server.address"))
       var inp = "".TaintedString
       server.listen()
       var stdoutSocket = newSocket()
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index 96d815df7..41cac2a4a 100644
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -26,6 +26,7 @@ type
     sym*: PSym
     unmatchedVarParam*, firstMismatch*: int
     diagnostics*: seq[string]
+    enabled*: bool
 
   CandidateErrors* = seq[CandidateError]
 
@@ -60,7 +61,8 @@ type
                               # matching. they will be reset if the matching
                               # is not successful. may replace the bindings
                               # table in the future.
-    diagnostics*: seq[string] # when this is not nil, the matching process
+    diagnostics*: seq[string] # \
+                              # when diagnosticsEnabled, the matching process
                               # will collect extra diagnostics that will be
                               # displayed to the user.
                               # triggered when overload resolution fails
@@ -70,6 +72,7 @@ type
     inheritancePenalty: int   # to prefer closest father object type
     firstMismatch*: int       # position of the first type mismatch for
                               # better error messages
+    diagnosticsEnabled*: bool
 
   TTypeRelFlag* = enum
     trDontBind
@@ -96,7 +99,7 @@ type
 const
   isNilConversion = isConvertible # maybe 'isIntConv' fits better?
 
-proc markUsed*(info: TLineInfo, s: PSym; usageSym: var PSym)
+proc markUsed*(conf: ConfigRef; info: TLineInfo, s: PSym; usageSym: var PSym)
 
 template hasFauxMatch*(c: TCandidate): bool = c.fauxMatch != tyNone
 
@@ -124,7 +127,8 @@ proc put(c: var TCandidate, key, val: PType) {.inline.} =
   idTablePut(c.bindings, key, val.skipIntLit)
 
 proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym,
-                    binding: PNode, calleeScope = -1, diagnostics = false) =
+                    binding: PNode, calleeScope = -1,
+                    diagnosticsEnabled = false) =
   initCandidateAux(ctx, c, callee.typ)
   c.calleeSym = callee
   if callee.kind in skProcKinds and calleeScope == -1:
@@ -139,7 +143,8 @@ proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym,
       c.calleeScope = 1
   else:
     c.calleeScope = calleeScope
-  c.diagnostics = if diagnostics: @[] else: nil
+  c.diagnostics = if diagnosticsEnabled: @[] else: nil
+  c.diagnosticsEnabled = diagnosticsEnabled
   c.magic = c.calleeSym.magic
   initIdTable(c.bindings)
   if binding != nil and callee.kind in routineKinds:
@@ -147,13 +152,13 @@ proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym,
     for i in 1..min(sonsLen(typeParams), sonsLen(binding)-1):
       var formalTypeParam = typeParams.sons[i-1].typ
       var bound = binding[i].typ
-      internalAssert bound != nil
-      if formalTypeParam.kind == tyTypeDesc:
-        if bound.kind != tyTypeDesc:
-          bound = makeTypeDesc(ctx, bound)
-      else:
-        bound = bound.skipTypes({tyTypeDesc})
-      put(c, formalTypeParam, bound)
+      if bound != nil:
+        if formalTypeParam.kind == tyTypeDesc:
+          if bound.kind != tyTypeDesc:
+            bound = makeTypeDesc(ctx, bound)
+        else:
+          bound = bound.skipTypes({tyTypeDesc})
+        put(c, formalTypeParam, bound)
 
 proc newCandidate*(ctx: PContext, callee: PSym,
                    binding: PNode, calleeScope = -1): TCandidate =
@@ -357,8 +362,8 @@ proc concreteType(c: TCandidate, t: PType): PType =
         # proc sort[T](cmp: proc(a, b: T): int = cmp)
       if result.kind != tyGenericParam: break
   of tyGenericInvocation:
-    internalError("cannot resolve type: " & typeToString(t))
     result = t
+    doAssert(false, "cannot resolve type: " & typeToString(t))
   else:
     result = t                # Note: empty is valid here
 
@@ -514,8 +519,8 @@ proc recordRel(c: var TCandidate, f, a: PType): TTypeRelation =
     if f.n != nil and a.n != nil:
       for i in countup(0, sonsLen(f.n) - 1):
         # check field names:
-        if f.n.sons[i].kind != nkSym: internalError(f.n.info, "recordRel")
-        elif a.n.sons[i].kind != nkSym: internalError(a.n.info, "recordRel")
+        if f.n.sons[i].kind != nkSym: return isNone
+        elif a.n.sons[i].kind != nkSym: return isNone
         else:
           var x = f.n.sons[i].sym
           var y = a.n.sons[i].sym
@@ -607,7 +612,7 @@ proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
     if tfNoSideEffect in f.flags and tfNoSideEffect notin a.flags:
       return isNone
     elif tfThread in f.flags and a.flags * {tfThread, tfNoSideEffect} == {} and
-        optThreadAnalysis in gGlobalOptions:
+        optThreadAnalysis in c.c.config.globalOptions:
       # noSideEffect implies ``tfThread``!
       return isNone
     elif f.flags * {tfIterator} != a.flags * {tfIterator}:
@@ -656,7 +661,7 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType =
     matchedConceptContext.prev = prevMatchedConcept
     matchedConceptContext.depth = prevMatchedConcept.depth + 1
     if prevMatchedConcept.depth > 4:
-      localError(body.info, $body & " too nested for type matching")
+      localError(m.c.graph.config, body.info, $body & " too nested for type matching")
       return nil
 
   openScope(c)
@@ -681,7 +686,7 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType =
       if alreadyBound != nil: typ = alreadyBound
 
       template paramSym(kind): untyped =
-        newSym(kind, typeParamName, typeClass.sym, typeClass.sym.info)
+        newSym(kind, typeParamName, typeClass.sym, typeClass.sym.info, {})
 
       block addTypeParam:
         for prev in typeParams:
@@ -717,7 +722,7 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType =
     diagnostics: seq[string]
     errorPrefix: string
     flags: TExprFlags = {}
-    collectDiagnostics = m.diagnostics != nil or
+    collectDiagnostics = m.diagnosticsEnabled or
                          sfExplain in typeClass.sym.flags
 
   if collectDiagnostics:
@@ -736,7 +741,9 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType =
 
   if collectDiagnostics:
     writelnHook = oldWriteHook
-    for msg in diagnostics: m.diagnostics.safeAdd msg
+    for msg in diagnostics:
+      m.diagnostics.safeAdd msg
+      m.diagnosticsEnabled = true
 
   if checkedBody == nil: return nil
 
@@ -753,19 +760,20 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType =
 
   result.n = checkedBody
 
-proc shouldSkipDistinct(rules: PNode, callIdent: PIdent): bool =
+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 r.considerQuotedIdent == callIdent: return true
+      if considerQuotedIdent(m.c.graph.config, r) == callIdent: return true
     return false
   else:
     for r in rules:
-      if r.considerQuotedIdent == callIdent: return false
+      if considerQuotedIdent(m.c.graph.config, r) == callIdent: return false
     return true
 
-proc maybeSkipDistinct(t: PType, callee: PSym): PType =
+proc maybeSkipDistinct(m: TCandidate; t: PType, callee: PSym): PType =
   if t != nil and t.kind == tyDistinct and t.n != nil and
-     shouldSkipDistinct(t.n, callee.name):
+     shouldSkipDistinct(m, t.n, callee.name):
     result = t.base
   else:
     result = t
@@ -861,11 +869,11 @@ proc inferStaticParam*(c: var TCandidate, lhs: PNode, rhs: BiggestInt): bool =
 
   return false
 
-proc failureToInferStaticParam(n: PNode) =
+proc failureToInferStaticParam(conf: ConfigRef; n: PNode) =
   let staticParam = n.findUnresolvedStatic
   let name = if staticParam != nil: staticParam.sym.name.s
              else: "unknown"
-  localError(n.info, errCannotInferStaticParam, name)
+  localError(conf, n.info, "cannot infer the value of the static param '" & name & "'")
 
 proc inferStaticsInRange(c: var TCandidate,
                          inferred, concrete: PType): TTypeRelation =
@@ -879,7 +887,7 @@ proc inferStaticsInRange(c: var TCandidate,
     if inferStaticParam(c, exp, rhs):
       return isGeneric
     else:
-      failureToInferStaticParam exp
+      failureToInferStaticParam(c.c.graph.config, exp)
 
   if lowerBound.kind == nkIntLit:
     if upperBound.kind == nkIntLit:
@@ -992,7 +1000,8 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
         of tyStatic:
           candidate = computedType
         else:
-          localError(f.n.info, errTypeExpected)
+          # XXX What is this non-sense? Error reporting in signature matching?
+          discard "localError(f.n.info, errTypeExpected)"
       else:
         discard
 
@@ -1006,7 +1015,7 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
   template doBind: bool = trDontBind notin flags
 
   # var and static arguments match regular modifier-free types
-  var a = aOrig.skipTypes({tyStatic, tyVar, tyLent}).maybeSkipDistinct(c.calleeSym)
+  var a = maybeSkipDistinct(c, aOrig.skipTypes({tyStatic, tyVar, tyLent}), c.calleeSym)
   # XXX: Theoretically, maybeSkipDistinct could be called before we even
   # start the param matching process. This could be done in `prepareOperand`
   # for example, but unfortunately `prepareOperand` is not called in certain
@@ -1388,8 +1397,13 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
 
         var aAsObject = roota.lastSon
 
-        if fKind in {tyRef, tyPtr} and aAsObject.kind == fKind:
-          aAsObject = aAsObject.base
+        if fKind in {tyRef, tyPtr}:
+          if aAsObject.kind == tyObject:
+            # bug #7600, tyObject cannot be passed
+            # as argument to tyRef/tyPtr
+            return isNone
+          elif aAsObject.kind == fKind:
+            aAsObject = aAsObject.base
 
         if aAsObject.kind == tyObject:
           let baseType = aAsObject.base
@@ -1430,7 +1444,7 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
           (sonsLen(x) - 1 == sonsLen(f)):
       for i in countup(1, sonsLen(f) - 1):
         if x.sons[i].kind == tyGenericParam:
-          internalError("wrong instantiated type!")
+          internalError(c.c.graph.config, "wrong instantiated type!")
         elif typeRel(c, f.sons[i], x.sons[i]) <= isSubtype:
           # Workaround for regression #4589
           if f.sons[i].kind != tyTypeDesc: return
@@ -1462,7 +1476,7 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
           if x == nil:
             discard "maybe fine (for eg. a==tyNil)"
           elif x.kind in {tyGenericInvocation, tyGenericParam}:
-            internalError("wrong instantiated type!")
+            internalError(c.c.graph.config, "wrong instantiated type!")
           else:
             put(c, f.sons[i], x)
 
@@ -1585,7 +1599,7 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
           if f.sonsLen == 0:
             result = isGeneric
           else:
-            internalAssert a.sons != nil and a.sons.len > 0
+            internalAssert c.c.graph.config, a.sons != nil and a.sons.len > 0
             c.typedescMatched = true
             var aa = a
             while aa.kind in {tyTypeDesc, tyGenericParam} and aa.len > 0:
@@ -1727,13 +1741,13 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
         if not exprStructuralEquivalent(aOrig.n, reevaluated.typ.n):
           result = isNone
     else:
-      localError(f.n.info, errTypeExpected)
+      localError(c.c.graph.config, f.n.info, "type expected")
       result = isNone
 
   of tyNone:
     if a.kind == tyNone: result = isEqual
   else:
-    internalError " unknown type kind " & $f.kind
+    internalError c.c.graph.config, " unknown type kind " & $f.kind
 
 proc cmpTypes*(c: PContext, f, a: PType): TTypeRelation =
   var m: TCandidate
@@ -1746,7 +1760,7 @@ proc getInstantiatedType(c: PContext, arg: PNode, m: TCandidate,
   if result == nil:
     result = generateTypeInstance(c, m.bindings, arg, f)
   if result == nil:
-    internalError(arg.info, "getInstantiatedType")
+    internalError(c.graph.config, arg.info, "getInstantiatedType")
     result = errorType(c)
 
 proc implicitConv(kind: TNodeKind, f: PType, arg: PNode, m: TCandidate,
@@ -1759,7 +1773,7 @@ proc implicitConv(kind: TNodeKind, f: PType, arg: PNode, m: TCandidate,
       result.typ = errorType(c)
   else:
     result.typ = f
-  if result.typ == nil: internalError(arg.info, "implicitConv")
+  if result.typ == nil: internalError(c.graph.config, arg.info, "implicitConv")
   addSon(result, ast.emptyNode)
   addSon(result, arg)
 
@@ -1780,7 +1794,7 @@ proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType,
       dest = generateTypeInstance(c, m.bindings, arg, dest)
     let fdest = typeRel(m, f, dest)
     if fdest in {isEqual, isGeneric}:
-      markUsed(arg.info, c.converters[i], c.graph.usageSym)
+      markUsed(c.config, arg.info, c.converters[i], c.graph.usageSym)
       var s = newSymNode(c.converters[i])
       s.typ = c.converters[i].typ
       s.info = arg.info
@@ -1950,7 +1964,8 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
     inc(m.genericMatches)
     if arg.typ == nil:
       result = arg
-    elif skipTypes(arg.typ, abstractVar-{tyTypeDesc}).kind == tyTuple:
+    elif skipTypes(arg.typ, abstractVar-{tyTypeDesc}).kind == tyTuple or
+         m.inheritancePenalty > 0:
       result = implicitConv(nkHiddenSubConv, f, arg, m, c)
     elif arg.typ.isEmptyContainer:
       result = arg.copyTree
@@ -2020,8 +2035,9 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType,
     y.calleeSym = m.calleeSym
     z.calleeSym = m.calleeSym
     var best = -1
-    for i in countup(0, sonsLen(arg) - 1):
-      if arg.sons[i].sym.kind in {skProc, skFunc, skMethod, skConverter, skIterator}:
+    for i in 0 ..< arg.len:
+      if arg.sons[i].sym.kind in {skProc, skFunc, skMethod, skConverter,
+                                  skIterator, skMacro, skTemplate}:
         copyCandidate(z, m)
         z.callee = arg.sons[i].typ
         if tfUnresolved in z.callee.flags: continue
@@ -2050,24 +2066,24 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType,
               x = z
             elif cmp == 0:
               y = z           # z is as good as x
+
     if x.state == csEmpty:
       result = nil
     elif y.state == csMatch and cmpCandidates(x, y) == 0:
       if x.state != csMatch:
-        internalError(arg.info, "x.state is not csMatch")
+        internalError(m.c.graph.config, arg.info, "x.state is not csMatch")
       # ambiguous: more than one symbol fits!
       # See tsymchoice_for_expr as an example. 'f.kind == tyExpr' should match
       # anyway:
-      if f.kind == tyExpr: result = arg
+      if f.kind in {tyExpr, tyStmt}: result = arg
       else: result = nil
     else:
       # only one valid interpretation found:
-      markUsed(arg.info, arg.sons[best].sym, m.c.graph.usageSym)
+      markUsed(m.c.config, arg.info, arg.sons[best].sym, m.c.graph.usageSym)
       styleCheckUse(arg.info, arg.sons[best].sym)
       result = paramTypesMatchAux(m, f, arg.sons[best].typ, arg.sons[best],
                                   argOrig)
 
-
 proc setSon(father: PNode, at: int, son: PNode) =
   let oldLen = father.len
   if oldLen <= at:
@@ -2103,10 +2119,10 @@ proc prepareOperand(c: PContext; a: PNode): PNode =
     result = a
     considerGenSyms(c, result)
 
-proc prepareNamedParam(a: PNode) =
+proc prepareNamedParam(a: PNode; conf: ConfigRef) =
   if a.sons[0].kind != nkIdent:
     var info = a.sons[0].info
-    a.sons[0] = newIdentNode(considerQuotedIdent(a.sons[0]), info)
+    a.sons[0] = newIdentNode(considerQuotedIdent(conf, a.sons[0]), info)
 
 proc arrayConstr(c: PContext, n: PNode): PType =
   result = newTypeS(tyArray, c)
@@ -2159,7 +2175,8 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
   var formal: PSym = if formalLen > 1: m.callee.n.sons[1].sym else: nil
 
   while a < n.len:
-    if a >= formalLen-1 and formal != nil and formal.typ.isVarargsUntyped:
+    if a >= formalLen-1 and f < formalLen and m.callee.n[f].typ.isVarargsUntyped:
+      formal = m.callee.n.sons[f].sym
       incl(marker, formal.position)
       if container.isNil:
         container = newNodeIT(nkArgList, n.sons[a].info, arrayConstr(c, n.info))
@@ -2170,9 +2187,9 @@ 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])
+      prepareNamedParam(n.sons[a], c.config)
       if n.sons[a].sons[0].kind != nkIdent:
-        localError(n.sons[a].info, errNamedParamHasToBeIdent)
+        localError(c.config, n.sons[a].info, "named parameter has to be an identifier")
         m.state = csNoMatch
         return
       formal = getSymFromList(m.callee.n, n.sons[a].sons[0].ident, 1)
@@ -2215,8 +2232,9 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
           # we have no formal here to snoop at:
           n.sons[a] = prepareOperand(c, n.sons[a])
           if skipTypes(n.sons[a].typ, abstractVar-{tyTypeDesc}).kind==tyString:
-            addSon(m.call, implicitConv(nkHiddenStdConv, getSysType(tyCString),
-                                        copyTree(n.sons[a]), m, c))
+            addSon(m.call, implicitConv(nkHiddenStdConv,
+                  getSysType(c.graph, n.sons[a].info, tyCString),
+                  copyTree(n.sons[a]), m, c))
           else:
             addSon(m.call, copyTree(n.sons[a]))
         elif formal != nil and formal.typ.kind == tyVarargs:
@@ -2239,7 +2257,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
           return
       else:
         if m.callee.n.sons[f].kind != nkSym:
-          internalError(n.sons[a].info, "matches")
+          internalError(c.config, n.sons[a].info, "matches")
           return
         formal = m.callee.n.sons[f].sym
         if containsOrIncl(marker, formal.position) and container.isNil:
@@ -2347,7 +2365,7 @@ proc instTypeBoundOp*(c: PContext; dc: PSym; t: PType; info: TLineInfo;
   var m: TCandidate
   initCandidate(c, m, dc.typ)
   if col >= dc.typ.len:
-    localError(info, errGenerated, "cannot instantiate '" & dc.name.s & "'")
+    localError(c.config, info, "cannot instantiate: '" & dc.name.s & "'")
     return nil
   var f = dc.typ.sons[col]
 
@@ -2356,7 +2374,7 @@ proc instTypeBoundOp*(c: PContext; dc: PSym; t: PType; info: TLineInfo;
   else:
     if f.kind == tyVar: f = f.lastSon
   if typeRel(m, f, t) == isNone:
-    localError(info, errGenerated, "cannot instantiate '" & dc.name.s & "'")
+    localError(c.config, info, "cannot instantiate: '" & dc.name.s & "'")
   else:
     result = c.semGenerateInstance(c, dc, m.bindings, info)
     if op == attachedDeepCopy:
diff --git a/compiler/suggest.nim b/compiler/suggest.nim
index af31495aa..23aecfa71 100644
--- a/compiler/suggest.nim
+++ b/compiler/suggest.nim
@@ -32,7 +32,7 @@
 
 # included from sigmatch.nim
 
-import algorithm, prefixmatches
+import algorithm, prefixmatches, configuration
 from wordrecg import wDeprecated
 
 when defined(nimsuggest):
@@ -106,7 +106,7 @@ proc cmpSuggestions(a, b: Suggest): int =
   # independent of hashing order:
   result = cmp(a.name.s, b.name.s)
 
-proc symToSuggest(s: PSym, isLocal: bool, section: IdeCmd, info: TLineInfo;
+proc symToSuggest(conf: ConfigRef; s: PSym, isLocal: bool, section: IdeCmd, info: TLineInfo;
                   quality: range[0..100]; prefix: PrefixMatch;
                   inTypeContext: bool; scope: int): Suggest =
   new(result)
@@ -125,7 +125,7 @@ proc symToSuggest(s: PSym, isLocal: bool, section: IdeCmd, info: TLineInfo;
       if u.fileIndex == info.fileIndex: inc c
     result.localUsages = c
   result.symkind = s.kind
-  if optIdeTerse notin gGlobalOptions:
+  if optIdeTerse notin conf.globalOptions:
     result.qualifiedPath = @[]
     if not isLocal and s.kind != skModule:
       let ow = s.owner
@@ -192,8 +192,8 @@ proc suggestResult(s: Suggest) =
   else:
     suggestWriteln($s)
 
-proc produceOutput(a: var Suggestions) =
-  if gIdeCmd in {ideSug, ideCon}:
+proc produceOutput(a: var Suggestions; conf: ConfigRef) =
+  if conf.ideCmd in {ideSug, ideCon}:
     a.sort cmpSuggestions
   when defined(debug):
     # debug code
@@ -237,7 +237,7 @@ proc fieldVisible*(c: PContext, f: PSym): bool {.inline.} =
 proc suggestField(c: PContext, s: PSym; f: PNode; info: TLineInfo; outputs: var Suggestions) =
   var pm: PrefixMatch
   if filterSym(s, f, pm) and fieldVisible(c, s):
-    outputs.add(symToSuggest(s, isLocal=true, ideSug, info, 100, pm, c.inTypeContext > 0, 0))
+    outputs.add(symToSuggest(c.config, s, isLocal=true, ideSug, info, 100, pm, c.inTypeContext > 0, 0))
 
 proc getQuality(s: PSym): range[0..100] =
   if s.typ != nil and s.typ.len > 1:
@@ -256,7 +256,7 @@ template wholeSymTab(cond, section: untyped) =
       let it {.inject.} = item
       var pm {.inject.}: PrefixMatch
       if cond:
-        outputs.add(symToSuggest(it, isLocal = isLocal, section, info, getQuality(it),
+        outputs.add(symToSuggest(c.config, it, isLocal = isLocal, section, info, getQuality(it),
                                  pm, c.inTypeContext > 0, scopeN))
 
 proc suggestSymList(c: PContext, list, f: PNode; info: TLineInfo, outputs: var Suggestions) =
@@ -330,7 +330,7 @@ proc suggestEverything(c: PContext, n, f: PNode, outputs: var Suggestions) =
     for it in items(scope.symbols):
       var pm: PrefixMatch
       if filterSym(it, f, pm):
-        outputs.add(symToSuggest(it, isLocal = isLocal, ideSug, n.info, 0, pm,
+        outputs.add(symToSuggest(c.config, it, isLocal = isLocal, ideSug, n.info, 0, pm,
                                  c.inTypeContext > 0, scopeN))
     #if scope == c.topLevelScope and f.isNil: break
 
@@ -342,18 +342,18 @@ proc suggestFieldAccess(c: PContext, n, field: PNode, outputs: var Suggestions)
   when defined(nimsuggest):
     if n.kind == nkSym and n.sym.kind == skError and suggestVersion == 0:
       # consider 'foo.|' where 'foo' is some not imported module.
-      let fullPath = findModule(n.sym.name.s, n.info.toFullPath)
+      let fullPath = findModule(c.config, n.sym.name.s, n.info.toFullPath)
       if fullPath.len == 0:
         # error: no known module name:
         typ = nil
       else:
-        let m = gImportModule(c.graph, c.module, fullpath.fileInfoIdx, c.cache)
+        let m = gImportModule(c.graph, c.module, fileInfoIdx(c.config, fullpath), c.cache)
         if m == nil: typ = nil
         else:
           for it in items(n.sym.tab):
             if filterSym(it, field, pm):
-              outputs.add(symToSuggest(it, isLocal=false, ideSug, n.info, 100, pm, c.inTypeContext > 0, -100))
-          outputs.add(symToSuggest(m, isLocal=false, ideMod, n.info, 100, PrefixMatch.None,
+              outputs.add(symToSuggest(c.config, it, isLocal=false, ideSug, n.info, 100, pm, c.inTypeContext > 0, -100))
+          outputs.add(symToSuggest(c.config, m, isLocal=false, ideMod, n.info, 100, PrefixMatch.None,
             c.inTypeContext > 0, -99))
 
   if typ == nil:
@@ -363,11 +363,11 @@ proc suggestFieldAccess(c: PContext, n, field: PNode, outputs: var Suggestions)
         # all symbols accessible, because we are in the current module:
         for it in items(c.topLevelScope.symbols):
           if filterSym(it, field, pm):
-            outputs.add(symToSuggest(it, isLocal=false, ideSug, n.info, 100, pm, c.inTypeContext > 0, -99))
+            outputs.add(symToSuggest(c.config, it, isLocal=false, ideSug, n.info, 100, pm, c.inTypeContext > 0, -99))
       else:
         for it in items(n.sym.tab):
           if filterSym(it, field, pm):
-            outputs.add(symToSuggest(it, isLocal=false, ideSug, n.info, 100, pm, c.inTypeContext > 0, -99))
+            outputs.add(symToSuggest(c.config, it, isLocal=false, ideSug, n.info, 100, pm, c.inTypeContext > 0, -99))
     else:
       # fallback:
       suggestEverything(c, n, field, outputs)
@@ -415,7 +415,7 @@ when defined(nimsuggest):
   # Since TLineInfo defined a == operator that doesn't include the column,
   # we map TLineInfo to a unique int here for this lookup table:
   proc infoToInt(info: TLineInfo): int64 =
-    info.fileIndex + info.line.int64 shl 32 + info.col.int64 shl 48
+    info.fileIndex.int64 + info.line.int64 shl 32 + info.col.int64 shl 48
 
   proc addNoDup(s: PSym; info: TLineInfo) =
     # ensure nothing gets too slow:
@@ -426,29 +426,29 @@ when defined(nimsuggest):
     s.allUsages.add(info)
 
 var
-  lastLineInfo*: TLineInfo
+  lastLineInfo*: TLineInfo # XXX global here
 
-proc findUsages(info: TLineInfo; s: PSym; usageSym: var PSym) =
+proc findUsages(conf: ConfigRef; info: TLineInfo; s: PSym; usageSym: var PSym) =
   if suggestVersion == 1:
     if usageSym == nil and isTracked(info, s.name.s.len):
       usageSym = s
-      suggestResult(symToSuggest(s, isLocal=false, ideUse, info, 100, PrefixMatch.None, false, 0))
+      suggestResult(symToSuggest(conf, s, isLocal=false, ideUse, info, 100, PrefixMatch.None, false, 0))
     elif s == usageSym:
       if lastLineInfo != info:
-        suggestResult(symToSuggest(s, isLocal=false, ideUse, info, 100, PrefixMatch.None, false, 0))
+        suggestResult(symToSuggest(conf, s, isLocal=false, ideUse, info, 100, PrefixMatch.None, false, 0))
       lastLineInfo = info
 
 when defined(nimsuggest):
-  proc listUsages*(s: PSym) =
+  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(s, isLocal=false, x, info, 100, PrefixMatch.None, false, 0))
+      suggestResult(symToSuggest(conf, s, isLocal=false, x, info, 100, PrefixMatch.None, false, 0))
 
-proc findDefinition(info: TLineInfo; s: PSym) =
+proc findDefinition(conf: ConfigRef; info: TLineInfo; s: PSym) =
   if s.isNil: return
   if isTracked(info, s.name.s.len):
-    suggestResult(symToSuggest(s, isLocal=false, ideDef, info, 100, PrefixMatch.None, false, 0))
+    suggestResult(symToSuggest(conf, s, isLocal=false, ideDef, info, 100, PrefixMatch.None, false, 0))
     suggestQuit()
 
 proc ensureIdx[T](x: var T, y: int) =
@@ -457,7 +457,7 @@ proc ensureIdx[T](x: var T, y: int) =
 proc ensureSeq[T](x: var seq[T]) =
   if x == nil: newSeq(x, 0)
 
-proc suggestSym*(info: TLineInfo; s: PSym; usageSym: var PSym; isDecl=true) {.inline.} =
+proc suggestSym*(conf: ConfigRef; info: TLineInfo; s: PSym; usageSym: var PSym; isDecl=true) {.inline.} =
   ## misnamed: should be 'symDeclared'
   when defined(nimsuggest):
     if suggestVersion == 0:
@@ -466,44 +466,44 @@ proc suggestSym*(info: TLineInfo; s: PSym; usageSym: var PSym; isDecl=true) {.in
       else:
         s.addNoDup(info)
 
-    if gIdeCmd == ideUse:
-      findUsages(info, s, usageSym)
-    elif gIdeCmd == ideDef:
-      findDefinition(info, s)
-    elif gIdeCmd == ideDus and s != nil:
+    if conf.ideCmd == ideUse:
+      findUsages(conf, info, s, usageSym)
+    elif conf.ideCmd == ideDef:
+      findDefinition(conf, info, s)
+    elif conf.ideCmd == ideDus and s != nil:
       if isTracked(info, s.name.s.len):
-        suggestResult(symToSuggest(s, isLocal=false, ideDef, info, 100, PrefixMatch.None, false, 0))
-      findUsages(info, s, usageSym)
-    elif gIdeCmd == ideHighlight and info.fileIndex == gTrackPos.fileIndex:
-      suggestResult(symToSuggest(s, isLocal=false, ideHighlight, info, 100, PrefixMatch.None, false, 0))
-    elif gIdeCmd == ideOutline and info.fileIndex == gTrackPos.fileIndex and
+        suggestResult(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
         isDecl:
-      suggestResult(symToSuggest(s, isLocal=false, ideOutline, info, 100, PrefixMatch.None, false, 0))
+      suggestResult(symToSuggest(conf, s, isLocal=false, ideOutline, info, 100, PrefixMatch.None, false, 0))
 
-proc warnAboutDeprecated(info: TLineInfo; s: PSym) =
+proc warnAboutDeprecated(conf: ConfigRef; info: TLineInfo; s: PSym) =
   if s.kind in routineKinds:
     let n = s.ast[pragmasPos]
     if n.kind != nkEmpty:
       for it in n:
         if whichPragma(it) == wDeprecated and it.safeLen == 2 and
             it[1].kind in {nkStrLit..nkTripleStrLit}:
-          message(info, warnDeprecated, it[1].strVal & "; " & s.name.s)
+          message(conf, info, warnDeprecated, it[1].strVal & "; " & s.name.s)
           return
-  message(info, warnDeprecated, s.name.s)
+  message(conf, info, warnDeprecated, s.name.s)
 
-proc markUsed(info: TLineInfo; s: PSym; usageSym: var PSym) =
+proc markUsed(conf: ConfigRef; info: TLineInfo; s: PSym; usageSym: var PSym) =
   incl(s.flags, sfUsed)
   if s.kind == skEnumField and s.owner != nil:
     incl(s.owner.flags, sfUsed)
   if {sfDeprecated, sfError} * s.flags != {}:
-    if sfDeprecated in s.flags: warnAboutDeprecated(info, s)
-    if sfError in s.flags: localError(info, errWrongSymbolX, s.name.s)
+    if sfDeprecated in s.flags: warnAboutDeprecated(conf, info, s)
+    if sfError in s.flags: localError(conf, info,  "usage of '$1' is a user-defined error" % s.name.s)
   when defined(nimsuggest):
-    suggestSym(info, s, usageSym, false)
+    suggestSym(conf, info, s, usageSym, false)
 
-proc useSym*(sym: PSym; usageSym: var PSym): PNode =
+proc useSym*(conf: ConfigRef; sym: PSym; usageSym: var PSym): PNode =
   result = newSymNode(sym)
-  markUsed(result.info, sym, usageSym)
+  markUsed(conf, result.info, sym, usageSym)
 
 proc safeSemExpr*(c: PContext, n: PNode): PNode =
   # use only for idetools support!
@@ -534,9 +534,9 @@ proc suggestExprNoCheck*(c: PContext, n: PNode) =
   if c.compilesContextId > 0: return
   inc(c.compilesContextId)
   var outputs: Suggestions = @[]
-  if gIdeCmd == ideSug:
+  if c.config.ideCmd == ideSug:
     sugExpr(c, n, outputs)
-  elif gIdeCmd == ideCon:
+  elif c.config.ideCmd == ideCon:
     if n.kind in nkCallKinds:
       var a = copyNode(n)
       var x = safeSemExpr(c, n.sons[0])
@@ -550,8 +550,8 @@ proc suggestExprNoCheck*(c: PContext, n: PNode) =
       suggestCall(c, a, n, outputs)
 
   dec(c.compilesContextId)
-  if outputs.len > 0 and gIdeCmd in {ideSug, ideCon, ideDef}:
-    produceOutput(outputs)
+  if outputs.len > 0 and c.config.ideCmd in {ideSug, ideCon, ideDef}:
+    produceOutput(outputs, c.config)
     suggestQuit()
 
 proc suggestExpr*(c: PContext, n: PNode) =
@@ -570,11 +570,11 @@ proc suggestStmt*(c: PContext, n: PNode) =
 proc suggestEnum*(c: PContext; n: PNode; t: PType) =
   var outputs: Suggestions = @[]
   suggestSymList(c, t.n, nil, n.info, outputs)
-  produceOutput(outputs)
+  produceOutput(outputs, c.config)
   if outputs.len > 0: suggestQuit()
 
 proc suggestSentinel*(c: PContext) =
-  if gIdeCmd != ideSug or c.module.position != gTrackPos.fileIndex: return
+  if c.config.ideCmd != ideSug or c.module.position != gTrackPos.fileIndex.int32: return
   if c.compilesContextId > 0: return
   inc(c.compilesContextId)
   var outputs: Suggestions = @[]
@@ -587,7 +587,7 @@ proc suggestSentinel*(c: PContext) =
     for it in items(scope.symbols):
       var pm: PrefixMatch
       if filterSymNoOpr(it, nil, pm):
-        outputs.add(symToSuggest(it, isLocal = isLocal, ideSug, newLineInfo(gTrackPos.fileIndex, -1, -1), 0, PrefixMatch.None, false, scopeN))
+        outputs.add(symToSuggest(c.config, it, isLocal = isLocal, ideSug, newLineInfo(gTrackPos.fileIndex, -1, -1), 0, PrefixMatch.None, false, scopeN))
 
   dec(c.compilesContextId)
-  produceOutput(outputs)
+  produceOutput(outputs, c.config)
diff --git a/compiler/syntaxes.nim b/compiler/syntaxes.nim
index 4745b1ac7..4bc153e46 100644
--- a/compiler/syntaxes.nim
+++ b/compiler/syntaxes.nim
@@ -11,17 +11,17 @@
 
 import
   strutils, llstream, ast, astalgo, idents, lexer, options, msgs, parser,
-  pbraces, filters, filter_tmpl, renderer
+  filters, filter_tmpl, renderer, configuration
 
 type
   TFilterKind* = enum
     filtNone, filtTemplate, filtReplace, filtStrip
   TParserKind* = enum
-    skinStandard, skinStrongSpaces, skinBraces, skinEndX
+    skinStandard, skinStrongSpaces, skinEndX
 
 const
   parserNames*: array[TParserKind, string] = ["standard", "strongspaces",
-                                              "braces", "endx"]
+                                              "endx"]
   filterNames*: array[TFilterKind, string] = ["none", "stdtmpl", "replace",
                                               "strip"]
 
@@ -30,39 +30,38 @@ type
     skin*: TParserKind
     parser*: TParser
 
+template config(p: TParsers): ConfigRef = p.parser.lex.config
+
 proc parseAll*(p: var TParsers): PNode =
   case p.skin
   of skinStandard, skinStrongSpaces:
     result = parser.parseAll(p.parser)
-  of skinBraces:
-    result = pbraces.parseAll(p.parser)
   of skinEndX:
-    internalError("parser to implement")
+    internalError(p.config, "parser to implement")
     result = ast.emptyNode
 
 proc parseTopLevelStmt*(p: var TParsers): PNode =
   case p.skin
   of skinStandard, skinStrongSpaces:
     result = parser.parseTopLevelStmt(p.parser)
-  of skinBraces:
-    result = pbraces.parseTopLevelStmt(p.parser)
   of skinEndX:
-    internalError("parser to implement")
+    internalError(p.config, "parser to implement")
     result = ast.emptyNode
 
 proc utf8Bom(s: string): int =
-  if s[0] == '\xEF' and s[1] == '\xBB' and s[2] == '\xBF':
+  if s.len >= 3 and s[0] == '\xEF' and s[1] == '\xBB' and s[2] == '\xBF':
     result = 3
   else:
     result = 0
 
 proc containsShebang(s: string, i: int): bool =
-  if s[i] == '#' and s[i+1] == '!':
+  if i+1 < s.len and s[i] == '#' and s[i+1] == '!':
     var j = i + 2
-    while s[j] in Whitespace: inc(j)
+    while j < s.len and s[j] in Whitespace: inc(j)
     result = s[j] == '/'
 
-proc parsePipe(filename: string, inputStream: PLLStream; cache: IdentCache): PNode =
+proc parsePipe(filename: string, inputStream: PLLStream; cache: IdentCache;
+               config: ConfigRef): PNode =
   result = ast.emptyNode
   var s = llStreamOpen(filename, fmRead)
   if s != nil:
@@ -74,11 +73,11 @@ proc parsePipe(filename: string, inputStream: PLLStream; cache: IdentCache): PNo
       discard llStreamReadLine(s, line)
       i = 0
       inc linenumber
-    if line[i] == '#' and line[i+1] == '?':
+    if i+1 < line.len and line[i] == '#' and line[i+1] == '?':
       inc(i, 2)
-      while line[i] in Whitespace: inc(i)
+      while i < line.len and line[i] in Whitespace: inc(i)
       var q: TParser
-      parser.openParser(q, filename, llStreamOpen(substr(line, i)), cache)
+      parser.openParser(q, filename, llStreamOpen(substr(line, i)), cache, config)
       result = parser.parseAll(q)
       parser.closeParser(q)
     llStreamClose(s)
@@ -89,42 +88,44 @@ proc getFilter(ident: PIdent): TFilterKind =
       return i
   result = filtNone
 
-proc getParser(ident: PIdent): TParserKind =
+proc getParser(conf: ConfigRef; n: PNode; ident: PIdent): TParserKind =
   for i in countup(low(TParserKind), high(TParserKind)):
     if cmpIgnoreStyle(ident.s, parserNames[i]) == 0:
       return i
-  rawMessage(errInvalidDirectiveX, ident.s)
+  localError(conf, n.info, "unknown parser: " & ident.s)
 
-proc getCallee(n: PNode): PIdent =
+proc getCallee(conf: ConfigRef; n: PNode): PIdent =
   if n.kind in nkCallKinds and n.sons[0].kind == nkIdent:
     result = n.sons[0].ident
   elif n.kind == nkIdent:
     result = n.ident
   else:
-    rawMessage(errXNotAllowedHere, renderTree(n))
+    localError(conf, n.info, "invalid filter: " & renderTree(n))
 
 proc applyFilter(p: var TParsers, n: PNode, filename: string,
                  stdin: PLLStream): PLLStream =
-  var ident = getCallee(n)
+  var ident = getCallee(p.config, n)
   var f = getFilter(ident)
   case f
   of filtNone:
-    p.skin = getParser(ident)
+    p.skin = getParser(p.config, n, ident)
     result = stdin
   of filtTemplate:
-    result = filterTmpl(stdin, filename, n)
+    result = filterTmpl(stdin, filename, n, p.config)
   of filtStrip:
-    result = filterStrip(stdin, filename, n)
+    result = filterStrip(p.config, stdin, filename, n)
   of filtReplace:
-    result = filterReplace(stdin, filename, n)
+    result = filterReplace(p.config, stdin, filename, n)
   if f != filtNone:
-    if hintCodeBegin in gNotes:
-      rawMessage(hintCodeBegin, [])
-      msgWriteln(result.s)
-      rawMessage(hintCodeEnd, [])
+    assert p.config != nil
+    if hintCodeBegin in p.config.notes:
+      rawMessage(p.config, hintCodeBegin, [])
+      msgWriteln(p.config, result.s)
+      rawMessage(p.config, hintCodeEnd, [])
 
 proc evalPipe(p: var TParsers, n: PNode, filename: string,
               start: PLLStream): PLLStream =
+  assert p.config != nil
   result = start
   if n.kind == nkEmpty: return
   if n.kind == nkInfix and n[0].kind == nkIdent and n[0].ident.s == "|":
@@ -138,31 +139,33 @@ proc evalPipe(p: var TParsers, n: PNode, filename: string,
   else:
     result = applyFilter(p, n, filename, result)
 
-proc openParsers*(p: var TParsers, fileIdx: int32, inputstream: PLLStream;
-                  cache: IdentCache) =
+proc openParsers*(p: var TParsers, fileIdx: FileIndex, inputstream: PLLStream;
+                  cache: IdentCache; config: ConfigRef) =
+  assert config != nil
   var s: PLLStream
   p.skin = skinStandard
   let filename = fileIdx.toFullPathConsiderDirty
-  var pipe = parsePipe(filename, inputstream, cache)
+  var pipe = parsePipe(filename, inputstream, cache, config)
+  p.config() = config
   if pipe != nil: s = evalPipe(p, pipe, filename, inputstream)
   else: s = inputstream
   case p.skin
-  of skinStandard, skinBraces, skinEndX:
-    parser.openParser(p.parser, fileIdx, s, cache, false)
+  of skinStandard, skinEndX:
+    parser.openParser(p.parser, fileIdx, s, cache, config, false)
   of skinStrongSpaces:
-    parser.openParser(p.parser, fileIdx, s, cache, true)
+    parser.openParser(p.parser, fileIdx, s, cache, config, true)
 
 proc closeParsers*(p: var TParsers) =
   parser.closeParser(p.parser)
 
-proc parseFile*(fileIdx: int32; cache: IdentCache): PNode {.procvar.} =
+proc parseFile*(fileIdx: FileIndex; cache: IdentCache; config: ConfigRef): PNode {.procvar.} =
   var
     p: TParsers
     f: File
   let filename = fileIdx.toFullPathConsiderDirty
   if not open(f, filename):
-    rawMessage(errCannotOpenFile, filename)
+    rawMessage(config, errGenerated, "cannot open file: " & filename)
     return
-  openParsers(p, fileIdx, llStreamOpen(f), cache)
+  openParsers(p, fileIdx, llStreamOpen(f), cache, config)
   result = parseAll(p)
   closeParsers(p)
diff --git a/compiler/transf.nim b/compiler/transf.nim
index f30f8583a..a10e8a1e5 100644
--- a/compiler/transf.nim
+++ b/compiler/transf.nim
@@ -21,7 +21,8 @@
 import
   intsets, strutils, options, ast, astalgo, trees, treetab, msgs, os,
   idents, renderer, types, passes, semfold, magicsys, cgmeth, rodread,
-  lambdalifting, sempass2, lowerings, lookups, destroyer, liftlocals
+  lambdalifting, sempass2, lowerings, lookups, destroyer, liftlocals,
+  modulegraphs
 
 type
   PTransNode* = distinct PNode
@@ -44,6 +45,7 @@ type
     nestedProcs: int         # > 0 if we are in a nested proc
     contSyms, breakSyms: seq[PSym]  # to transform 'continue' and 'break'
     deferDetected, tooEarly, needsDestroyPass: bool
+    graph: ModuleGraph
   PTransf = ref TTransfContext
 
 proc newTransNode(a: PNode): PTransNode {.inline.} =
@@ -84,7 +86,7 @@ proc pushTransCon(c: PTransf, t: PTransCon) =
   c.transCon = t
 
 proc popTransCon(c: PTransf) =
-  if (c.transCon == nil): internalError("popTransCon")
+  if (c.transCon == nil): internalError(c.graph.config, "popTransCon")
   c.transCon = c.transCon.next
 
 proc getCurrOwner(c: PTransf): PSym =
@@ -97,7 +99,7 @@ proc newTemp(c: PTransf, typ: PType, info: TLineInfo): PNode =
   incl(r.flags, sfFromGeneric)
   let owner = getCurrOwner(c)
   if owner.isIterator and not c.tooEarly:
-    result = freshVarForClosureIter(r, owner)
+    result = freshVarForClosureIter(c.graph, r, owner)
   else:
     result = newSymNode(r)
 
@@ -118,10 +120,10 @@ proc transformSymAux(c: PTransf, n: PNode): PNode =
   if s.typ != nil and s.typ.callConv == ccClosure:
     if s.kind == skIterator:
       if c.tooEarly: return n
-      else: return liftIterSym(n, getCurrOwner(c))
+      else: return liftIterSym(c.graph, n, getCurrOwner(c))
     elif s.kind in {skProc, skFunc, skConverter, skMethod} and not c.tooEarly:
       # top level .closure procs are still somewhat supported for 'Nake':
-      return makeClosure(s, nil, n.info)
+      return makeClosure(c.graph, s, nil, n.info)
   #elif n.sym.kind in {skVar, skLet} and n.sym.typ.callConv == ccClosure:
   #  echo n.info, " come heer for ", c.tooEarly
   #  if not c.tooEarly:
@@ -130,7 +132,7 @@ proc transformSymAux(c: PTransf, n: PNode): PNode =
   if sfBorrow in s.flags and s.kind in routineKinds:
     # simply exchange the symbol:
     b = s.getBody
-    if b.kind != nkSym: internalError(n.info, "wrong AST for borrowed symbol")
+    if b.kind != nkSym: internalError(c.graph.config, n.info, "wrong AST for borrowed symbol")
     b = newSymNode(b.sym, n.info)
   else:
     b = n
@@ -151,7 +153,7 @@ proc transformSym(c: PTransf, n: PNode): PTransNode =
 proc freshVar(c: PTransf; v: PSym): PNode =
   let owner = getCurrOwner(c)
   if owner.isIterator and not c.tooEarly:
-    result = freshVarForClosureIter(v, owner)
+    result = freshVarForClosureIter(c.graph, v, owner)
   else:
     var newVar = copySym(v)
     incl(newVar.flags, sfFromGeneric)
@@ -166,11 +168,11 @@ proc transformVarSection(c: PTransf, v: PNode): PTransNode =
       result[i] = PTransNode(it)
     elif it.kind == nkIdentDefs:
       if it.sons[0].kind == nkSym:
-        internalAssert(it.len == 3)
+        internalAssert(c.graph.config, it.len == 3)
         let x = freshVar(c, it.sons[0].sym)
         idNodeTablePut(c.transCon.mapping, it.sons[0].sym, x)
         var defs = newTransNode(nkIdentDefs, it.info, 3)
-        if importantComments():
+        if importantComments(c.graph.config):
           # keep documentation information:
           PNode(defs).comment = it.comment
         defs[0] = x.PTransNode
@@ -184,7 +186,7 @@ proc transformVarSection(c: PTransf, v: PNode): PTransNode =
         result[i] = transform(c, it)
     else:
       if it.kind != nkVarTuple:
-        internalError(it.info, "transformVarSection: not nkVarTuple")
+        internalError(c.graph.config, it.info, "transformVarSection: not nkVarTuple")
       var L = sonsLen(it)
       var defs = newTransNode(it.kind, it.info, L)
       for j in countup(0, L-3):
@@ -203,9 +205,9 @@ proc transformConstSection(c: PTransf, v: PNode): PTransNode =
     if it.kind == nkCommentStmt:
       result[i] = PTransNode(it)
     else:
-      if it.kind != nkConstDef: internalError(it.info, "transformConstSection")
+      if it.kind != nkConstDef: internalError(c.graph.config, it.info, "transformConstSection")
       if it.sons[0].kind != nkSym:
-        internalError(it.info, "transformConstSection")
+        internalError(c.graph.config, it.info, "transformConstSection")
 
       result[i] = PTransNode(it)
 
@@ -301,7 +303,7 @@ proc unpackTuple(c: PTransf, n: PNode, father: PTransNode) =
   # XXX: BUG: what if `n` is an expression with side-effects?
   for i in countup(0, sonsLen(c.transCon.forStmt) - 3):
     add(father, newAsgnStmt(c, c.transCon.forStmt.sons[i],
-        transform(c, newTupleAccess(n, i))))
+        transform(c, newTupleAccess(c.graph, n, i))))
 
 proc introduceNewLocalVars(c: PTransf, n: PNode): PTransNode =
   case n.kind
@@ -356,7 +358,7 @@ proc transformYield(c: PTransf, n: PNode): PTransNode =
 
 proc transformAddrDeref(c: PTransf, n: PNode, a, b: TNodeKind): PTransNode =
   result = transformSons(c, n)
-  if gCmd == cmdCompileToCpp or sfCompileToCpp in c.module.flags: return
+  if c.graph.config.cmd == cmdCompileToCpp or sfCompileToCpp in c.module.flags: return
   var n = result.PNode
   case n.sons[0].kind
   of nkObjUpConv, nkObjDownConv, nkChckRange, nkChckRangeF, nkChckRange64:
@@ -382,21 +384,21 @@ proc transformAddrDeref(c: PTransf, n: PNode, a, b: TNodeKind): PTransNode =
       if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
         PNode(result).typ = n.typ
 
-proc generateThunk(prc: PNode, dest: PType): PNode =
+proc generateThunk(c: PTransf; prc: PNode, dest: PType): PNode =
   ## Converts 'prc' into '(thunk, nil)' so that it's compatible with
   ## a closure.
 
   # we cannot generate a proper thunk here for GC-safety reasons
   # (see internal documentation):
-  if gCmd == cmdCompileToJS: return prc
+  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(prc)
   if prc.kind == nkClosure:
-    internalError(prc.info, "closure to closure created")
+    internalError(c.graph.config, prc.info, "closure to closure created")
   result.add(conv)
-  result.add(newNodeIT(nkNilLit, prc.info, getSysType(tyNil)))
+  result.add(newNodeIT(nkNilLit, prc.info, getSysType(c.graph, prc.info, tyNil)))
 
 proc transformConv(c: PTransf, n: PNode): PTransNode =
   # numeric types need range checks:
@@ -481,7 +483,7 @@ proc transformConv(c: PTransf, n: PNode): PTransNode =
   of tyProc:
     result = transformSons(c, n)
     if dest.callConv == ccClosure and source.callConv == ccDefault:
-      result = generateThunk(result[1].PNode, dest).PTransNode
+      result = generateThunk(c, result[1].PNode, dest).PTransNode
   else:
     result = transformSons(c, n)
 
@@ -513,7 +515,7 @@ proc findWrongOwners(c: PTransf, n: PNode) =
   if n.kind == nkVarSection:
     let x = n.sons[0].sons[0]
     if x.kind == nkSym and x.sym.owner != getCurrOwner(c):
-      internalError(x.info, "bah " & x.sym.name.s & " " &
+      internalError(c.graph.config, x.info, "bah " & x.sym.name.s & " " &
         x.sym.owner.name.s & " " & getCurrOwner(c).name.s)
   else:
     for i in 0 ..< safeLen(n): findWrongOwners(c, n.sons[i])
@@ -521,7 +523,7 @@ proc findWrongOwners(c: PTransf, n: PNode) =
 proc transformFor(c: PTransf, n: PNode): PTransNode =
   # generate access statements for the parameters (unless they are constant)
   # put mapping from formal parameters to actual parameters
-  if n.kind != nkForStmt: internalError(n.info, "transformFor")
+  if n.kind != nkForStmt: internalError(c.graph.config, n.info, "transformFor")
 
   var length = sonsLen(n)
   var call = n.sons[length - 2]
@@ -539,7 +541,7 @@ proc transformFor(c: PTransf, n: PNode): PTransNode =
     n.sons[length-1] = transformLoopBody(c, n.sons[length-1]).PNode
     if not c.tooEarly:
       n.sons[length-2] = transform(c, n.sons[length-2]).PNode
-      result[1] = lambdalifting.liftForLoop(n, getCurrOwner(c)).PTransNode
+      result[1] = lambdalifting.liftForLoop(c.graph, n, getCurrOwner(c)).PTransNode
     else:
       result[1] = newNode(nkEmpty).PTransNode
     discard c.breakSyms.pop
@@ -689,7 +691,7 @@ proc transformCall(c: PTransf, n: PNode): PTransNode =
         while (j < sonsLen(n)):
           let b = transform(c, n.sons[j]).PNode
           if not isConstExpr(b): break
-          a = evalOp(op.magic, n, a, b, nil)
+          a = evalOp(op.magic, n, a, b, nil, c.graph)
           inc(j)
       add(result, a.PTransNode)
     if len(result) == 2: result = result[1]
@@ -708,18 +710,18 @@ proc transformCall(c: PTransf, n: PNode): PTransNode =
         let t = lastSon(s.sons[0].sym.ast)
         if t.kind != nkSym or sfDispatcher notin t.sym.flags:
           methodDef(s.sons[0].sym, false)
-      result = methodCall(s).PTransNode
+      result = methodCall(s, c.graph.config).PTransNode
     else:
       result = s.PTransNode
 
 proc transformExceptBranch(c: PTransf, n: PNode): PTransNode =
   result = transformSons(c, n)
-  if n[0].isInfixAs() and (not isImportedException(n[0][1].typ)):
+  if n[0].isInfixAs() and not isImportedException(n[0][1].typ, c.graph.config):
     let excTypeNode = n[0][1]
     let actions = newTransNode(nkStmtListExpr, n[1], 2)
     # Generating `let exc = (excType)(getCurrentException())`
     # -> getCurrentException()
-    let excCall = PTransNode(callCodegenProc("getCurrentException", ast.emptyNode))
+    let excCall = PTransNode(callCodegenProc(c.graph, "getCurrentException", ast.emptyNode))
     # -> (excType)
     let convNode = newTransNode(nkHiddenSubConv, n[1].info, 2)
     convNode[0] = PTransNode(ast.emptyNode)
@@ -748,10 +750,10 @@ proc dontInlineConstant(orig, cnst: PNode): bool {.inline.} =
   result = orig.kind == nkSym and cnst.kind in {nkCurly, nkPar, nkTupleConstr, nkBracket} and
       cnst.len != 0
 
-proc commonOptimizations*(c: PSym, n: PNode): PNode =
+proc commonOptimizations*(g: ModuleGraph; c: PSym, n: PNode): PNode =
   result = n
   for i in 0 ..< n.safeLen:
-    result.sons[i] = commonOptimizations(c, n.sons[i])
+    result.sons[i] = commonOptimizations(g, c, n.sons[i])
   var op = getMergeOp(n)
   if (op != nil) and (op.magic != mNone) and (sonsLen(n) >= 3):
     result = newNodeIT(nkCall, n.info, n.typ)
@@ -766,12 +768,12 @@ proc commonOptimizations*(c: PSym, n: PNode): PNode =
         while j < sonsLen(args):
           let b = args.sons[j]
           if not isConstExpr(b): break
-          a = evalOp(op.magic, result, a, b, nil)
+          a = evalOp(op.magic, result, a, b, nil, g)
           inc(j)
       add(result, a)
     if len(result) == 2: result = result[1]
   else:
-    var cnst = getConstExpr(c, n)
+    var cnst = getConstExpr(c, n, g)
     # we inline constants if they are not complex constants:
     if cnst != nil and not dontInlineConstant(n, cnst):
       result = cnst
@@ -888,7 +890,7 @@ proc transform(c: PTransf, n: PNode): PTransNode =
       let L = n.len-1
       result[L] = transform(c, n.sons[L])
     # XXX comment handling really sucks:
-    if importantComments():
+    if importantComments(c.graph.config):
       PNode(result).comment = n.comment
   of nkClosure:
     # it can happen that for-loop-inlining produced a fresh
@@ -905,7 +907,7 @@ proc transform(c: PTransf, n: PNode): PTransNode =
   when false:
     if oldDeferAnchor != nil: c.deferAnchor = oldDeferAnchor
 
-  var cnst = getConstExpr(c.module, PNode(result))
+  var cnst = getConstExpr(c.module, PNode(result), c.graph)
   # we inline constants if they are not complex constants:
   if cnst != nil and not dontInlineConstant(n, cnst):
     result = PTransNode(cnst) # do not miss an optimization
@@ -920,11 +922,12 @@ proc processTransf(c: PTransf, n: PNode, owner: PSym): PNode =
   popTransCon(c)
   incl(result.flags, nfTransf)
 
-proc openTransf(module: PSym, filename: string): PTransf =
+proc openTransf(g: ModuleGraph; module: PSym, filename: string): PTransf =
   new(result)
   result.contSyms = @[]
   result.breakSyms = @[]
   result.module = module
+  result.graph = g
 
 proc flattenStmts(n: PNode) =
   var goOn = true
@@ -967,46 +970,46 @@ template liftDefer(c, root) =
   if c.deferDetected:
     liftDeferAux(root)
 
-proc transformBody*(module: PSym, n: PNode, prc: PSym): PNode =
+proc transformBody*(g: ModuleGraph; module: PSym, n: PNode, prc: PSym): PNode =
   if nfTransf in n.flags or prc.kind in {skTemplate}:
     result = n
   else:
-    var c = openTransf(module, "")
-    result = liftLambdas(prc, n, c.tooEarly)
+    var c = openTransf(g, module, "")
+    result = liftLambdas(g, prc, n, c.tooEarly)
     #result = n
     result = processTransf(c, result, prc)
     liftDefer(c, result)
     #result = liftLambdas(prc, result)
-    when useEffectSystem: trackProc(prc, result)
-    result = liftLocalsIfRequested(prc, result)
-    if c.needsDestroyPass and newDestructors:
-      result = injectDestructorCalls(prc, result)
+    when useEffectSystem: trackProc(g, prc, result)
+    result = liftLocalsIfRequested(prc, result, g.config)
+    if c.needsDestroyPass: #and newDestructors:
+      result = injectDestructorCalls(g, prc, result)
     incl(result.flags, nfTransf)
       #if prc.name.s == "testbody":
     #  echo renderTree(result)
 
-proc transformStmt*(module: PSym, n: PNode): PNode =
+proc transformStmt*(g: ModuleGraph; module: PSym, n: PNode): PNode =
   if nfTransf in n.flags:
     result = n
   else:
-    var c = openTransf(module, "")
+    var c = openTransf(g, module, "")
     result = processTransf(c, n, module)
     liftDefer(c, result)
     #result = liftLambdasForTopLevel(module, result)
-    when useEffectSystem: trackTopLevelStmt(module, result)
+    when useEffectSystem: trackTopLevelStmt(g, module, result)
     #if n.info ?? "temp.nim":
     #  echo renderTree(result, {renderIds})
-    if c.needsDestroyPass and newDestructors:
-      result = injectDestructorCalls(module, result)
+    if c.needsDestroyPass:
+      result = injectDestructorCalls(g, module, result)
     incl(result.flags, nfTransf)
 
-proc transformExpr*(module: PSym, n: PNode): PNode =
+proc transformExpr*(g: ModuleGraph; module: PSym, n: PNode): PNode =
   if nfTransf in n.flags:
     result = n
   else:
-    var c = openTransf(module, "")
+    var c = openTransf(g, module, "")
     result = processTransf(c, n, module)
     liftDefer(c, result)
-    if c.needsDestroyPass and newDestructors:
-      result = injectDestructorCalls(module, result)
+    if c.needsDestroyPass:
+      result = injectDestructorCalls(g, module, result)
     incl(result.flags, nfTransf)
diff --git a/compiler/types.nim b/compiler/types.nim
index a930779bf..b5f4fbf54 100644
--- a/compiler/types.nim
+++ b/compiler/types.nim
@@ -10,7 +10,7 @@
 # this module contains routines for accessing and iterating over types
 
 import
-  intsets, ast, astalgo, trees, msgs, strutils, platform, renderer
+  intsets, ast, astalgo, trees, msgs, strutils, platform, renderer, options
 
 type
   TPreferedDesc* = enum
@@ -92,8 +92,9 @@ proc getOrdValue*(n: PNode): BiggestInt =
   of nkNilLit: result = 0
   of nkHiddenStdConv: result = getOrdValue(n.sons[1])
   else:
-    localError(n.info, errOrdinalTypeExpected)
-    result = 0
+    #localError(n.info, errOrdinalTypeExpected)
+    # XXX check usages of getOrdValue
+    result = high(BiggestInt)
 
 proc isIntLit*(t: PType): bool {.inline.} =
   result = t.kind == tyInt and t.n != nil and t.n.kind == nkIntLit
@@ -105,14 +106,14 @@ proc getProcHeader*(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):
-    var p = n.sons[i]
+    let p = n.sons[i]
     if p.kind == nkSym:
       add(result, p.sym.name.s)
       add(result, ": ")
       add(result, typeToString(p.sym.typ, prefer))
       if i != sonsLen(n)-1: add(result, ", ")
     else:
-      internalError("getProcHeader")
+      result.add renderTree(p)
   add(result, ')')
   if n.sons[0].typ != nil:
     result.add(": " & typeToString(n.sons[0].typ, prefer))
@@ -195,10 +196,10 @@ proc searchTypeNodeForAux(n: PNode, p: TTypePredicate,
       of nkOfBranch, nkElse:
         result = searchTypeNodeForAux(lastSon(n.sons[i]), p, marker)
         if result: return
-      else: internalError("searchTypeNodeForAux(record case branch)")
+      else: discard
   of nkSym:
     result = searchTypeForAux(n.sym.typ, p, marker)
-  else: internalError(n.info, "searchTypeNodeForAux()")
+  else: discard
 
 proc searchTypeForAux(t: PType, predicate: TTypePredicate,
                       marker: var IntSet): bool =
@@ -244,7 +245,7 @@ proc analyseObjectWithTypeFieldAux(t: PType,
   if t == nil: return
   case t.kind
   of tyObject:
-    if (t.n != nil):
+    if t.n != nil:
       if searchTypeNodeForAux(t.n, isObjectWithTypeFieldPredicate, marker):
         return frEmbedded
     for i in countup(0, sonsLen(t) - 1):
@@ -453,16 +454,17 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
     if t.sons[0].kind == tyNone: result = "typedesc"
     else: result = "type " & typeToString(t.sons[0])
   of tyStatic:
-    internalAssert t.len > 0
     if prefer == preferGenericArg and t.n != nil:
       result = t.n.renderTree
     else:
-      result = "static[" & typeToString(t.sons[0]) & "]"
+      result = "static[" & (if t.len > 0: typeToString(t.sons[0]) else: "") & "]"
       if t.n != nil: result.add "(" & renderTree(t.n) & ")"
   of tyUserTypeClass:
-    internalAssert t.sym != nil and t.sym.owner != nil
-    if t.isResolvedUserTypeClass: return typeToString(t.lastSon)
-    return t.sym.owner.name.s
+    if t.sym != nil and t.sym.owner != nil:
+      if t.isResolvedUserTypeClass: return typeToString(t.lastSon)
+      return t.sym.owner.name.s
+    else:
+      result = "<invalid tyUserTypeClass>"
   of tyBuiltInTypeClass:
     result = case t.base.kind:
       of tyVar: "var"
@@ -496,7 +498,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
   of tyNot:
     result = "not " & typeToString(t.sons[0])
   of tyExpr:
-    internalAssert t.len == 0
+    #internalAssert t.len == 0
     result = "untyped"
   of tyFromExpr:
     result = renderTree(t.n)
@@ -612,13 +614,13 @@ proc firstOrd*(t: PType): BiggestInt =
     else:
       assert(t.n.sons[0].kind == nkSym)
       result = t.n.sons[0].sym.position
-  of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias:
+  of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tyStatic, tyInferred:
     result = firstOrd(lastSon(t))
   of tyOrdinal:
     if t.len > 0: result = firstOrd(lastSon(t))
-    else: internalError("invalid kind for first(" & $t.kind & ')')
+    else: internalError(newPartialConfigRef(), "invalid kind for firstOrd(" & $t.kind & ')')
   else:
-    internalError("invalid kind for first(" & $t.kind & ')')
+    internalError(newPartialConfigRef(), "invalid kind for firstOrd(" & $t.kind & ')')
     result = 0
 
 proc lastOrd*(t: PType; fixedUnsigned = false): BiggestInt =
@@ -651,14 +653,14 @@ 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:
+  of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tyStatic, tyInferred:
     result = lastOrd(lastSon(t))
   of tyProxy: result = 0
   of tyOrdinal:
     if t.len > 0: result = lastOrd(lastSon(t))
-    else: internalError("invalid kind for last(" & $t.kind & ')')
+    else: internalError(newPartialConfigRef(), "invalid kind for lastOrd(" & $t.kind & ')')
   else:
-    internalError("invalid kind for last(" & $t.kind & ')')
+    internalError(newPartialConfigRef(), "invalid kind for lastOrd(" & $t.kind & ')')
     result = 0
 
 proc lengthOrd*(t: PType): BiggestInt =
@@ -748,7 +750,7 @@ proc equalParam(a, b: PSym): TParamsEquality =
 
 proc sameConstraints(a, b: PNode): bool =
   if isNil(a) and isNil(b): return true
-  internalAssert a.len == b.len
+  if a.len != b.len: return false
   for i in 1 ..< a.len:
     if not exprStructuralEquivalent(a[i].sym.constraint,
                                     b[i].sym.constraint):
@@ -777,8 +779,8 @@ proc equalParams(a, b: PNode): TParamsEquality =
         return paramsNotEqual # paramsIncompatible;
       # continue traversal! If not equal, we can return immediately; else
       # it stays incompatible
-    if not sameTypeOrNil(a.sons[0].typ, b.sons[0].typ, {ExactTypeDescValues}):
-      if (a.sons[0].typ == nil) or (b.sons[0].typ == nil):
+    if not sameTypeOrNil(a.typ, b.typ, {ExactTypeDescValues}):
+      if (a.typ == nil) or (b.typ == nil):
         result = paramsNotEqual # one proc has a result, the other not is OK
       else:
         result = paramsIncompatible # overloading by different
@@ -807,7 +809,8 @@ proc sameTuple(a, b: PType, c: var TSameTypeClosure): bool =
           var y = b.n.sons[i].sym
           result = x.name.id == y.name.id
           if not result: break
-        else: internalError(a.n.info, "sameTuple")
+        else:
+          return false
     elif a.n != b.n and (a.n == nil or b.n == nil) and IgnoreTupleFields notin c.flags:
       result = false
 
@@ -970,7 +973,7 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
       result = sameFlags(a, b)
   of tyGenericParam:
     result = sameChildrenAux(a, b, c) and sameFlags(a, b)
-    if result and ExactGenericParams in c.flags:
+    if result and {ExactGenericParams, ExactTypeDescValues} * c.flags != {}:
       result = a.sym.position == b.sym.position
   of tyGenericInvocation, tyGenericBody, tySequence,
      tyOpenArray, tySet, tyRef, tyPtr, tyVar, tyLent, tySink,
@@ -997,7 +1000,7 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
     cycleCheck()
     result = sameTypeAux(a.lastSon, b.lastSon, c)
   of tyNone: result = false
-  of tyUnused, tyOptAsRef: internalError("sameFlags")
+  of tyUnused, tyOptAsRef: result = false
 
 proc sameBackendType*(x, y: PType): bool =
   var c = initSameTypeClosure()
@@ -1191,7 +1194,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
     # for now same as error node; we say it's a valid type as it should
     # prevent cascading errors:
     result = nil
-  of tyUnused, tyOptAsRef: internalError("typeAllowedAux")
+  of tyUnused, tyOptAsRef: result = t
 
 proc typeAllowed*(t: PType, kind: TSymKind; flags: TTypeAllowedFlags = {}): PType =
   # returns 'nil' on success and otherwise the part of the type that is
@@ -1280,7 +1283,8 @@ proc computeRecSizeAux(n: PNode, a, currOffset: var BiggestInt): BiggestInt =
         if res < 0: return res
         maxSize = max(maxSize, res)
         maxAlign = max(maxAlign, b)
-      else: internalError("computeRecSizeAux(record case branch)")
+      else:
+        return szIllegalRecursion
     currOffset = align(currOffset, maxAlign) + maxSize
     result = align(result, maxAlign) + maxSize
     a = maxAlign
@@ -1441,7 +1445,8 @@ proc getReturnType*(s: PSym): PType =
 
 proc getSize*(typ: PType): BiggestInt =
   result = computeSize(typ)
-  if result < 0: internalError("getSize: " & $typ.kind)
+  #if result < 0: internalError("getSize: " & $typ.kind)
+  # XXX review all usages of 'getSize'
 
 proc containsGenericTypeIter(t: PType, closure: RootRef): bool =
   case t.kind
@@ -1469,8 +1474,7 @@ proc baseOfDistinct*(t: PType): PType =
     while it.kind in {tyPtr, tyRef}:
       parent = it
       it = it.lastSon
-    if it.kind == tyDistinct:
-      internalAssert parent != nil
+    if it.kind == tyDistinct and parent != nil:
       parent.sons[0] = it.sons[0]
 
 proc safeInheritanceDiff*(a, b: PType): int =
@@ -1502,8 +1506,9 @@ type
 proc compatibleEffects*(formal, actual: PType): EffectsCompat =
   # for proc type compatibility checking:
   assert formal.kind == tyProc and actual.kind == tyProc
-  internalAssert formal.n.sons[0].kind == nkEffectList
-  internalAssert actual.n.sons[0].kind == nkEffectList
+  if formal.n.sons[0].kind != nkEffectList or
+     actual.n.sons[0].kind != nkEffectList:
+    return efTagsUnknown
 
   var spec = formal.n.sons[0]
   if spec.len != 0:
@@ -1628,14 +1633,14 @@ proc skipHiddenSubConv*(n: PNode): PNode =
   else:
     result = n
 
-proc typeMismatch*(info: TLineInfo, formal, actual: PType) =
+proc typeMismatch*(conf: ConfigRef; info: TLineInfo, formal, actual: PType) =
   if formal.kind != tyError and actual.kind != tyError:
     let named = typeToString(formal)
     let desc = typeToString(formal, preferDesc)
     let x = if named == desc: named else: named & " = " & desc
-    var msg = msgKindToString(errTypeMismatch) &
+    var msg = "type mismatch: got <" &
               typeToString(actual) & "> " &
-              msgKindToString(errButExpectedX) % [x]
+              "but expected '" & x & "'"
 
     if formal.kind == tyProc and actual.kind == tyProc:
       case compatibleEffects(formal, actual)
@@ -1650,4 +1655,4 @@ proc typeMismatch*(info: TLineInfo, formal, actual: PType) =
         msg.add "\n.tag effect is 'any tag allowed'"
       of efLockLevelsDiffer:
         msg.add "\nlock levels differ"
-    localError(info, errGenerated, msg)
+    localError(conf, info, msg)
diff --git a/compiler/typesrenderer.nim b/compiler/typesrenderer.nim
index 4a9b8d1a9..4d75d5d05 100644
--- a/compiler/typesrenderer.nim
+++ b/compiler/typesrenderer.nim
@@ -17,7 +17,6 @@ proc renderPlainSymbolName*(n: PNode): string =
   ## Use this on documentation name nodes to extract the *raw* symbol name,
   ## without decorations, parameters, or anything. That can be used as the base
   ## for the HTML hyperlinks.
-  result = ""
   case n.kind
   of nkPostfix, nkAccQuoted:
     result = renderPlainSymbolName(n[n.len-1])
@@ -28,7 +27,8 @@ proc renderPlainSymbolName*(n: PNode): string =
   of nkPragmaExpr:
     result = renderPlainSymbolName(n[0])
   else:
-    internalError(n.info, "renderPlainSymbolName() with " & $n.kind)
+    result = ""
+    #internalError(n.info, "renderPlainSymbolName() with " & $n.kind)
   assert(not result.isNil)
 
 proc renderType(n: PNode): string =
@@ -105,7 +105,8 @@ proc renderParamTypes(found: var seq[string], n: PNode) =
     for i in 0 ..< typePos:
       found.add(typeStr)
   else:
-    internalError(n.info, "renderParamTypes(found,n) with " & $n.kind)
+    found.add($n)
+    #internalError(n.info, "renderParamTypes(found,n) with " & $n.kind)
 
 proc renderParamTypes*(n: PNode, sep = defaultParamSeparator): string =
   ## Returns the types contained in `n` joined by `sep`.
diff --git a/compiler/vm.nim b/compiler/vm.nim
index 09226988a..cbd304caa 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
+  vmmarshal, gorgeimpl, configuration
 
 from semfold import leValueConv, ordinalValToString
 from evaltempl import evalTemplate
@@ -61,7 +61,7 @@ proc stackTraceAux(c: PCtx; x: PStackFrame; pc: int; recursionLimit=100) =
       while x != nil:
         inc calls
         x = x.next
-      msgWriteln($calls & " calls omitted\n")
+      msgWriteln(c.config, $calls & " calls omitted\n")
       return
     stackTraceAux(c, x.next, x.comesFrom, recursionLimit-1)
     var info = c.debug[pc]
@@ -78,19 +78,19 @@ proc stackTraceAux(c: PCtx; x: PStackFrame; pc: int; recursionLimit=100) =
     if x.prc != nil:
       for k in 1..max(1, 25-s.len): add(s, ' ')
       add(s, x.prc.name.s)
-    msgWriteln(s)
+    msgWriteln(c.config, s)
 
 proc stackTrace(c: PCtx, tos: PStackFrame, pc: int,
-                msg: TMsgKind, arg = "", n: PNode = nil) =
-  msgWriteln("stack trace: (most recent call last)")
+                msg: string, n: PNode = nil) =
+  msgWriteln(c.config, "stack trace: (most recent call last)")
   stackTraceAux(c, tos, pc)
   # XXX test if we want 'globalError' for every mode
   let lineInfo = if n == nil: c.debug[pc] else: n.info
-  if c.mode == emRepl: globalError(lineInfo, msg, arg)
-  else: localError(lineInfo, msg, arg)
+  if c.mode == emRepl: globalError(c.config, lineInfo, msg)
+  else: localError(c.config, lineInfo, msg)
 
 proc bailOut(c: PCtx; tos: PStackFrame) =
-  stackTrace(c, tos, c.exceptionInstr, errUnhandledExceptionX,
+  stackTrace(c, tos, c.exceptionInstr, "unhandled exception: " &
              c.currentExceptionA.sons[3].skipColon.strVal)
 
 when not defined(nimComputedGoto):
@@ -210,8 +210,14 @@ proc putIntoNode(n: var PNode; x: TFullReg) =
   of rkInt: n.intVal = x.intVal
   of rkFloat: n.floatVal = x.floatVal
   of rkNode:
-    if nfIsRef in x.node.flags: n = x.node
-    else: n[] = x.node[]
+    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[][]
 
@@ -308,7 +314,7 @@ proc cleanUpOnReturn(c: PCtx; f: PStackFrame): int =
       return pc
   return -1
 
-proc opConv*(dest: var TFullReg, src: TFullReg, desttyp, srctyp: PType): bool =
+proc opConv(c: PCtx; dest: var TFullReg, src: TFullReg, desttyp, srctyp: PType): bool =
   if desttyp.kind == tyString:
     if dest.kind != rkNode:
       myreset(dest)
@@ -323,7 +329,7 @@ proc opConv*(dest: var TFullReg, src: TFullReg, desttyp, srctyp: PType): bool =
         dest.node.strVal = if f.ast.isNil: f.name.s else: f.ast.strVal
       else:
         for i in 0..<n.len:
-          if n.sons[i].kind != nkSym: internalError("opConv for enum")
+          if n.sons[i].kind != nkSym: internalError(c.config, "opConv for enum")
           let f = n.sons[i].sym
           if f.position == x:
             dest.node.strVal = if f.ast.isNil: f.name.s else: f.ast.strVal
@@ -353,7 +359,7 @@ proc opConv*(dest: var TFullReg, src: TFullReg, desttyp, srctyp: PType): bool =
     of tyChar:
       dest.node.strVal = $chr(src.intVal)
     else:
-      internalError("cannot convert to string " & desttyp.typeToString)
+      internalError(c.config, "cannot convert to string " & desttyp.typeToString)
   else:
     case skipTypes(desttyp, abstractRange).kind
     of tyInt..tyInt64:
@@ -402,9 +408,9 @@ template handleJmpBack() {.dirty.} =
     if allowInfiniteLoops in c.features:
       c.loopIterations = MaxLoopIterations
     else:
-      msgWriteln("stack trace: (most recent call last)")
+      msgWriteln(c.config, "stack trace: (most recent call last)")
       stackTraceAux(c, tos, pc)
-      globalError(c.debug[pc], errTooManyIterations)
+      globalError(c.config, c.debug[pc], errTooManyIterations)
   dec(c.loopIterations)
 
 proc recSetFlagIsRef(arg: PNode) =
@@ -434,6 +440,17 @@ proc setLenSeq(c: PCtx; node: PNode; newLen: int; info: TLineInfo) =
     for i in oldLen ..< newLen:
       node.sons[i] = newNodeI(typeKind, info)
 
+const
+  errIndexOutOfBounds = "index out of bounds"
+  errNilAccess = "attempt to access a nil address"
+  errOverOrUnderflow = "over- or underflow"
+  errConstantDivisionByZero = "division by zero"
+  errIllegalConvFromXtoY = "illegal conversion from '$1' to '$2'"
+  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 = "node lacks field: "
+
 proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
   var pc = start
   var tos = tos
@@ -447,7 +464,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
     #if c.traceActive:
     when traceCode:
       echo "PC ", pc, " ", c.code[pc].opcode, " ra ", ra, " rb ", instr.regB, " rc ", instr.regC
-    #  message(c.debug[pc], warnUser, "Trace")
+    #  message(c.config, c.debug[pc], warnUser, "Trace")
 
     case instr.opcode
     of opcEof: return regs[ra]
@@ -580,7 +597,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       if regs[rb].kind == rkNode:
         regs[ra].nodeAddr = addr(regs[rb].node)
       else:
-        stackTrace(c, tos, pc, errGenerated, "limited VM support for 'addr'")
+        stackTrace(c, tos, pc, "limited VM support for 'addr'")
     of opcLdDeref:
       # a = b[]
       let ra = instr.regA
@@ -623,7 +640,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
         stackTrace(c, tos, pc, errOverOrUnderflow)
     of opcAddImmInt:
       decodeBImm(rkInt)
-      #message(c.debug[pc], warnUser, "came here")
+      #message(c.config, c.debug[pc], warnUser, "came here")
       #debug regs[rb].node
       let
         bVal = regs[rb].intVal
@@ -892,17 +909,17 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
         regs[ra].node = if a.sym.ast.isNil: newNode(nkNilLit)
                         else: copyTree(a.sym.ast)
       else:
-        stackTrace(c, tos, pc, errGenerated, "node is not a symbol")
+        stackTrace(c, tos, pc, "node is not a symbol")
     of opcEcho:
       let rb = instr.regB
       if rb == 1:
-        msgWriteln(regs[ra].node.strVal, {msgStdout})
+        msgWriteln(c.config, regs[ra].node.strVal, {msgStdout})
       else:
         var outp = ""
         for i in ra..ra+rb-1:
           #if regs[i].kind != rkNode: debug regs[i]
           outp.add(regs[i].node.strVal)
-        msgWriteln(outp, {msgStdout})
+        msgWriteln(c.config, outp, {msgStdout})
     of opcContainsSet:
       decodeBC(rkInt)
       regs[ra].intVal = ord(inSet(regs[rb].node, regs[rc].regToNode))
@@ -931,9 +948,9 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       let rc = instr.regC
       if not (leValueConv(regs[rb].regToNode, regs[ra].regToNode) and
               leValueConv(regs[ra].regToNode, regs[rc].regToNode)):
-        stackTrace(c, tos, pc, errGenerated,
-          msgKindToString(errIllegalConvFromXtoY) % [
-          $regs[ra].regToNode, "[" & $regs[rb].regToNode & ".." & $regs[rc].regToNode & "]"])
+        stackTrace(c, tos, pc,
+          errIllegalConvFromXtoY % [
+             $regs[ra].regToNode, "[" & $regs[rb].regToNode & ".." & $regs[rc].regToNode & "]"])
     of opcIndCall, opcIndCallAsgn:
       # dest = call regStart, n; where regStart = fn, arg1, ...
       let rb = instr.regB
@@ -949,20 +966,20 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
                  currentLineInfo: c.debug[pc]))
       elif sfImportc in prc.flags:
         if allowFFI notin c.features:
-          globalError(c.debug[pc], errGenerated, "VM not allowed to do FFI")
+          globalError(c.config, c.debug[pc], "VM not allowed to do FFI")
         # we pass 'tos.slots' instead of 'regs' so that the compiler can keep
         # 'regs' in a register:
         when hasFFI:
           let prcValue = c.globals.sons[prc.position-1]
           if prcValue.kind == nkEmpty:
-            globalError(c.debug[pc], errGenerated, "canot run " & prc.name.s)
+            globalError(c.config, c.debug[pc], "canot run " & prc.name.s)
           let newValue = callForeignFunction(prcValue, prc.typ, tos.slots,
                                              rb+1, rc-1, c.debug[pc])
           if newValue.kind != nkEmpty:
             assert instr.opcode == opcIndCallAsgn
             putIntoReg(regs[ra], newValue)
         else:
-          globalError(c.debug[pc], errGenerated, "VM not built with FFI support")
+          globalError(c.config, c.debug[pc], "VM not built with FFI support")
       elif prc.kind != skTemplate:
         let newPc = compile(c, prc)
         # tricky: a recursion is also a jump back, so we use the same
@@ -972,7 +989,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
         var newFrame = PStackFrame(prc: prc, comesFrom: pc, next: tos)
         newSeq(newFrame.slots, prc.offset+ord(isClosure))
         if not isEmptyType(prc.typ.sons[0]) or prc.kind == skMacro:
-          putIntoReg(newFrame.slots[0], getNullValue(prc.typ.sons[0], prc.info))
+          putIntoReg(newFrame.slots[0], getNullValue(prc.typ.sons[0], prc.info, c.config))
         for i in 1 .. rc-1:
           newFrame.slots[i] = regs[rb+i]
         if isClosure:
@@ -994,7 +1011,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
           let node = regs[rb+i].regToNode
           node.info = c.debug[pc]
           macroCall.add(node)
-        var a = evalTemplate(macroCall, prc, genSymOwner)
+        var a = evalTemplate(macroCall, prc, genSymOwner, c.config)
         if a.kind == nkStmtList and a.len == 1: a = a[0]
         a.recSetFlagIsRef
         ensureKind(rkNode)
@@ -1079,7 +1096,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
     of opcNew:
       ensureKind(rkNode)
       let typ = c.types[instr.regBx - wordExcess]
-      regs[ra].node = getNullValue(typ, c.debug[pc])
+      regs[ra].node = getNullValue(typ, c.debug[pc], c.config)
       regs[ra].node.flags.incl nfIsRef
     of opcNewSeq:
       let typ = c.types[instr.regBx - wordExcess]
@@ -1091,7 +1108,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       regs[ra].node.typ = typ
       newSeq(regs[ra].node.sons, count)
       for i in 0 ..< count:
-        regs[ra].node.sons[i] = getNullValue(typ.sons[0], c.debug[pc])
+        regs[ra].node.sons[i] = getNullValue(typ.sons[0], c.debug[pc], c.config)
     of opcNewStr:
       decodeB(rkNode)
       regs[ra].node = newNodeI(nkStrLit, c.debug[pc])
@@ -1103,7 +1120,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
     of opcLdNull:
       ensureKind(rkNode)
       let typ = c.types[instr.regBx - wordExcess]
-      regs[ra].node = getNullValue(typ, c.debug[pc])
+      regs[ra].node = getNullValue(typ, c.debug[pc], c.config)
       # opcLdNull really is the gist of the VM's problems: should it load
       # a fresh null to  regs[ra].node  or to regs[ra].node[]? This really
       # depends on whether regs[ra] represents the variable itself or wether
@@ -1150,7 +1167,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       regs[ra].node.strVal = renderTree(regs[rb].regToNode, {renderNoComments, renderDocComments})
     of opcQuit:
       if c.mode in {emRepl, emStaticExpr, emStaticStmt}:
-        message(c.debug[pc], hintQuitCalled)
+        message(c.config, c.debug[pc], hintQuitCalled)
         msgQuit(int8(getOrdValue(regs[ra].regToNode)))
       else:
         return TFullReg(kind: rkNone)
@@ -1176,14 +1193,13 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       if regs[ra].node.isNil: stackTrace(c, tos, pc, errNilAccess)
       else: c.setLenSeq(regs[ra].node, newLen, c.debug[pc])
     of opcReset:
-      internalError(c.debug[pc], "too implement")
+      internalError(c.config, c.debug[pc], "too implement")
     of opcNarrowS:
       decodeB(rkInt)
       let min = -(1.BiggestInt shl (rb-1))
       let max = (1.BiggestInt shl (rb-1))-1
       if regs[ra].intVal < min or regs[ra].intVal > max:
-        stackTrace(c, tos, pc, errGenerated,
-          msgKindToString(errUnhandledExceptionX) % "value out of range")
+        stackTrace(c, tos, pc, "unhandled exception: value out of range")
     of opcNarrowU:
       decodeB(rkInt)
       regs[ra].intVal = regs[ra].intVal and ((1'i64 shl rb)-1)
@@ -1217,7 +1233,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       if u.kind notin {nkEmpty..nkNilLit}:
         u.add(regs[rc].node)
       else:
-        stackTrace(c, tos, pc, errGenerated, "cannot add to node kind: " & $u.kind)
+        stackTrace(c, tos, pc, "cannot add to node kind: " & $u.kind)
       regs[ra].node = u
     of opcNAddMultiple:
       decodeBC(rkNode)
@@ -1227,7 +1243,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
         # XXX can be optimized:
         for i in 0..<x.len: u.add(x.sons[i])
       else:
-        stackTrace(c, tos, pc, errGenerated, "cannot add to node kind: " & $u.kind)
+        stackTrace(c, tos, pc, "cannot add to node kind: " & $u.kind)
       regs[ra].node = u
     of opcNKind:
       decodeB(rkInt)
@@ -1239,34 +1255,34 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       if a.kind == nkSym:
         regs[ra].intVal = ord(a.sym.kind)
       else:
-        stackTrace(c, tos, pc, errGenerated, "node is not a symbol")
+        stackTrace(c, tos, pc, "node is not a symbol")
       c.comesFromHeuristic = regs[rb].node.info
     of opcNIntVal:
       decodeB(rkInt)
       let a = regs[rb].node
       case a.kind
       of nkCharLit..nkUInt64Lit: regs[ra].intVal = a.intVal
-      else: stackTrace(c, tos, pc, errFieldXNotFound, "intVal")
+      else: stackTrace(c, tos, pc, errFieldXNotFound & "intVal")
     of opcNFloatVal:
       decodeB(rkFloat)
       let a = regs[rb].node
       case a.kind
       of nkFloatLit..nkFloat64Lit: regs[ra].floatVal = a.floatVal
-      else: stackTrace(c, tos, pc, errFieldXNotFound, "floatVal")
+      else: stackTrace(c, tos, pc, errFieldXNotFound & "floatVal")
     of opcNSymbol:
       decodeB(rkNode)
       let a = regs[rb].node
       if a.kind == nkSym:
         regs[ra].node = copyNode(a)
       else:
-        stackTrace(c, tos, pc, errFieldXNotFound, "symbol")
+        stackTrace(c, tos, pc, errFieldXNotFound & "symbol")
     of opcNIdent:
       decodeB(rkNode)
       let a = regs[rb].node
       if a.kind == nkIdent:
         regs[ra].node = copyNode(a)
       else:
-        stackTrace(c, tos, pc, errFieldXNotFound, "ident")
+        stackTrace(c, tos, pc, errFieldXNotFound & "ident")
     of opcNGetType:
       let rb = instr.regB
       let rc = instr.regC
@@ -1277,28 +1293,28 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
         if regs[rb].kind == rkNode and regs[rb].node.typ != nil:
           regs[ra].node = opMapTypeToAst(regs[rb].node.typ, c.debug[pc])
         else:
-          stackTrace(c, tos, pc, errGenerated, "node has no type")
+          stackTrace(c, tos, pc, "node has no type")
       of 1:
         # typeKind opcode:
         ensureKind(rkInt)
         if regs[rb].kind == rkNode and regs[rb].node.typ != nil:
           regs[ra].intVal = ord(regs[rb].node.typ.kind)
         #else:
-        #  stackTrace(c, tos, pc, errGenerated, "node has no type")
+        #  stackTrace(c, tos, pc, "node has no type")
       of 2:
         # 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])
         else:
-          stackTrace(c, tos, pc, errGenerated, "node has no type")
+          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])
         else:
-          stackTrace(c, tos, pc, errGenerated, "node has no type")
+          stackTrace(c, tos, pc, "node has no type")
     of opcNStrVal:
       decodeB(rkNode)
       createStr regs[ra]
@@ -1313,12 +1329,12 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       of nkSym:
         regs[ra].node.strVal = a.sym.name.s
       else:
-        stackTrace(c, tos, pc, errFieldXNotFound, "strVal")
+        stackTrace(c, tos, pc, errFieldXNotFound & "strVal")
     of opcSlurp:
       decodeB(rkNode)
       createStr regs[ra]
       regs[ra].node.strVal = opSlurp(regs[rb].node.strVal, c.debug[pc],
-                                     c.module)
+                                     c.module, c.config)
     of opcGorge:
       decodeBC(rkNode)
       inc pc
@@ -1327,39 +1343,40 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       createStr regs[ra]
       regs[ra].node.strVal = opGorge(regs[rb].node.strVal,
                                      regs[rc].node.strVal, regs[rd].node.strVal,
-                                     c.debug[pc])[0]
+                                     c.debug[pc], c.config)[0]
     of opcNError:
       decodeB(rkNode)
       let a = regs[ra].node
       let b = regs[rb].node
-      stackTrace(c, tos, pc, errUser, a.strVal, if b.kind == nkNilLit: nil else: b)
+      stackTrace(c, tos, pc, a.strVal, if b.kind == nkNilLit: nil else: b)
     of opcNWarning:
-      message(c.debug[pc], warnUser, regs[ra].node.strVal)
+      message(c.config, c.debug[pc], warnUser, regs[ra].node.strVal)
     of opcNHint:
-      message(c.debug[pc], hintUser, regs[ra].node.strVal)
+      message(c.config, c.debug[pc], hintUser, regs[ra].node.strVal)
     of opcParseExprToAst:
       decodeB(rkNode)
       # c.debug[pc].line.int - countLines(regs[rb].strVal) ?
       var error: string
-      let ast = parseString(regs[rb].node.strVal, c.cache, c.debug[pc].toFullPath,
-                            c.debug[pc].line.int,
-                            proc (info: TLineInfo; msg: TMsgKind; arg: string) =
-                              if error.isNil and msg <= msgs.errMax:
-                                error = formatMsg(info, msg, arg))
+      let ast = parseString(regs[rb].node.strVal, c.cache, c.config,
+                            c.debug[pc].toFullPath, 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))
       if not error.isNil:
         c.errorFlag = error
       elif sonsLen(ast) != 1:
-        c.errorFlag = formatMsg(c.debug[pc], errExprExpected, "multiple statements")
+        c.errorFlag = formatMsg(c.config, c.debug[pc], errGenerated,
+          "expected expression, but got multiple statements")
       else:
         regs[ra].node = ast.sons[0]
     of opcParseStmtToAst:
       decodeB(rkNode)
       var error: string
-      let ast = parseString(regs[rb].node.strVal, c.cache, c.debug[pc].toFullPath,
-                            c.debug[pc].line.int,
-                            proc (info: TLineInfo; msg: TMsgKind; arg: string) =
-                              if error.isNil and msg <= msgs.errMax:
-                                error = formatMsg(info, msg, arg))
+      let ast = parseString(regs[rb].node.strVal, c.cache, c.config,
+                            c.debug[pc].toFullPath, 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))
       if not error.isNil:
         c.errorFlag = error
       else:
@@ -1371,7 +1388,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
     of opcCallSite:
       ensureKind(rkNode)
       if c.callsite != nil: regs[ra].node = c.callsite
-      else: stackTrace(c, tos, pc, errFieldXNotFound, "callsite")
+      else: stackTrace(c, tos, pc, errFieldXNotFound & "callsite")
     of opcNGetFile:
       decodeB(rkNode)
       let n = regs[rb].node
@@ -1381,7 +1398,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
     of opcNGetLine:
       decodeB(rkNode)
       let n = regs[rb].node
-      regs[ra].node = newIntNode(nkIntLit, n.info.line)
+      regs[ra].node = newIntNode(nkIntLit, n.info.line.int)
       regs[ra].node.info = n.info
       regs[ra].node.typ = n.typ
     of opcNGetColumn:
@@ -1397,8 +1414,8 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       let bNode = regs[rc].node
       # these are cstring to prevent string copy, and cmpIgnoreStyle from
       # takes cstring arguments
-      var aStrVal: cstring
-      var bStrVal: cstring
+      var aStrVal: cstring = nil
+      var bStrVal: cstring = nil
       # extract strVal from argument ``a``
       case aNode.kind
       of {nkStrLit..nkTripleStrLit}:
@@ -1407,8 +1424,10 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
         aStrVal = aNode.ident.s.cstring
       of nkSym:
         aStrVal = aNode.sym.name.s.cstring
+      of nkOpenSymChoice, nkClosedSymChoice:
+        aStrVal = aNode[0].sym.name.s.cstring
       else:
-        stackTrace(c, tos, pc, errFieldXNotFound, "strVal")
+        discard
       # extract strVal from argument ``b``
       case bNode.kind
       of {nkStrLit..nkTripleStrLit}:
@@ -1417,21 +1436,27 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
         bStrVal = bNode.ident.s.cstring
       of nkSym:
         bStrVal = bNode.sym.name.s.cstring
+      of nkOpenSymChoice, nkClosedSymChoice:
+        bStrVal = bNode[0].sym.name.s.cstring
       else:
-        stackTrace(c, tos, pc, errFieldXNotFound, "strVal")
+        discard
       # set result
       regs[ra].intVal =
-        ord(idents.cmpIgnoreStyle(aStrVal,bStrVal,high(int)) == 0)
+        if aStrVal != nil and bStrVal != nil:
+          ord(idents.cmpIgnoreStyle(aStrVal,bStrVal,high(int)) == 0)
+        else:
+          0
+
     of opcStrToIdent:
       decodeB(rkNode)
       if regs[rb].node.kind notin {nkStrLit..nkTripleStrLit}:
-        stackTrace(c, tos, pc, errFieldXNotFound, "strVal")
+        stackTrace(c, tos, pc, errFieldXNotFound & "strVal")
       else:
         regs[ra].node = newNodeI(nkIdent, c.debug[pc])
         regs[ra].node.ident = getIdent(regs[rb].node.strVal)
     of opcSetType:
       if regs[ra].kind != rkNode:
-        internalError(c.debug[pc], "cannot set type")
+        internalError(c.config, c.debug[pc], "cannot set type")
       regs[ra].node.typ = c.types[instr.regBx - wordExcess]
     of opcConv:
       let rb = instr.regB
@@ -1440,9 +1465,9 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       inc pc
       let srctyp = c.types[c.code[pc].regBx - wordExcess]
 
-      if opConv(regs[ra], regs[rb], desttyp, srctyp):
-        stackTrace(c, tos, pc, errGenerated,
-          msgKindToString(errIllegalConvFromXtoY) % [
+      if opConv(c, regs[ra], regs[rb], desttyp, srctyp):
+        stackTrace(c, tos, pc,
+          errIllegalConvFromXtoY % [
           typeToString(srctyp), typeToString(desttyp)])
     of opcCast:
       let rb = instr.regB
@@ -1455,7 +1480,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
         let dest = fficast(regs[rb], desttyp)
         asgnRef(regs[ra], dest)
       else:
-        globalError(c.debug[pc], "cannot evaluate cast")
+        globalError(c.config, c.debug[pc], "cannot evaluate cast")
     of opcNSetIntVal:
       decodeB(rkNode)
       var dest = regs[ra].node
@@ -1463,7 +1488,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
          regs[rb].kind in {rkInt}:
         dest.intVal = regs[rb].intVal
       else:
-        stackTrace(c, tos, pc, errFieldXNotFound, "intVal")
+        stackTrace(c, tos, pc, errFieldXNotFound & "intVal")
     of opcNSetFloatVal:
       decodeB(rkNode)
       var dest = regs[ra].node
@@ -1471,26 +1496,26 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
          regs[rb].kind in {rkFloat}:
         dest.floatVal = regs[rb].floatVal
       else:
-        stackTrace(c, tos, pc, errFieldXNotFound, "floatVal")
+        stackTrace(c, tos, pc, errFieldXNotFound & "floatVal")
     of opcNSetSymbol:
       decodeB(rkNode)
       var dest = regs[ra].node
       if dest.kind == nkSym and regs[rb].node.kind == nkSym:
         dest.sym = regs[rb].node.sym
       else:
-        stackTrace(c, tos, pc, errFieldXNotFound, "symbol")
+        stackTrace(c, tos, pc, errFieldXNotFound & "symbol")
     of opcNSetIdent:
       decodeB(rkNode)
       var dest = regs[ra].node
       if dest.kind == nkIdent and regs[rb].node.kind == nkIdent:
         dest.ident = regs[rb].node.ident
       else:
-        stackTrace(c, tos, pc, errFieldXNotFound, "ident")
+        stackTrace(c, tos, pc, errFieldXNotFound & "ident")
     of opcNSetType:
       decodeB(rkNode)
       let b = regs[rb].node
-      internalAssert b.kind == nkSym and b.sym.kind == skType
-      internalAssert regs[ra].node != nil
+      internalAssert c.config, b.kind == nkSym and b.sym.kind == skType
+      internalAssert c.config, regs[ra].node != nil
       regs[ra].node.typ = b.sym.typ
     of opcNSetStrVal:
       decodeB(rkNode)
@@ -1501,19 +1526,19 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       elif dest.kind == nkCommentStmt and regs[rb].kind in {rkNode}:
         dest.comment = regs[rb].node.strVal
       else:
-        stackTrace(c, tos, pc, errFieldXNotFound, "strVal")
+        stackTrace(c, tos, pc, errFieldXNotFound & "strVal")
     of opcNNewNimNode:
       decodeBC(rkNode)
       var k = regs[rb].intVal
       if k < 0 or k > ord(high(TNodeKind)):
-        internalError(c.debug[pc],
+        internalError(c.config, c.debug[pc],
           "request to create a NimNode of invalid kind")
       let cc = regs[rc].node
 
       let x = newNodeI(TNodeKind(int(k)),
         if cc.kind != nkNilLit:
           cc.info
-        elif c.comesFromHeuristic.line > -1:
+        elif c.comesFromHeuristic.line != 0'u16:
           c.comesFromHeuristic
         elif c.callsite != nil and c.callsite.safeLen > 1:
           c.callsite[1].info
@@ -1540,7 +1565,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       let name = if regs[rc].node.strVal.len == 0: ":tmp"
                  else: regs[rc].node.strVal
       if k < 0 or k > ord(high(TSymKind)):
-        internalError(c.debug[pc], "request to create symbol of invalid kind")
+        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])
       incl(sym.flags, sfGenSym)
       regs[ra].node = newSymNode(sym)
@@ -1549,7 +1574,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       # type trait operation
       decodeB(rkNode)
       var typ = regs[rb].node.typ
-      internalAssert typ != nil
+      internalAssert c.config, typ != nil
       while typ.kind == tyTypeDesc and typ.len > 0: typ = typ.sons[0]
       createStr regs[ra]
       regs[ra].node.strVal = typ.typeToString(preferExported)
@@ -1558,14 +1583,14 @@ 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))
+      putIntoReg(regs[ra], loadAny(regs[rb].node.strVal, typ, c.config))
     of opcMarshalStore:
       decodeB(rkNode)
       inc pc
       let typ = c.types[c.code[pc].regBx - wordExcess]
       createStrKeepNode(regs[ra])
       if regs[ra].node.strVal.isNil: regs[ra].node.strVal = newStringOfCap(1000)
-      storeAny(regs[ra].node.strVal, typ, regs[rb].regToNode)
+      storeAny(regs[ra].node.strVal, typ, regs[rb].regToNode, c.config)
     of opcToNarrowInt:
       decodeBC(rkInt)
       let mask = (1'i64 shl rc) - 1 # 0xFF
@@ -1587,7 +1612,7 @@ proc execute(c: PCtx, start: int): PNode =
 proc execProc*(c: PCtx; sym: PSym; args: openArray[PNode]): PNode =
   if sym.kind in routineKinds:
     if sym.typ.len-1 != args.len:
-      localError(sym.info,
+      localError(c.config, sym.info,
         "NimScript: expected $# arguments, but got $#" % [
         $(sym.typ.len-1), $args.len])
     else:
@@ -1599,18 +1624,18 @@ proc execProc*(c: PCtx; sym: PSym; args: openArray[PNode]): PNode =
 
       # setup parameters:
       if not isEmptyType(sym.typ.sons[0]) or sym.kind == skMacro:
-        putIntoReg(tos.slots[0], getNullValue(sym.typ.sons[0], sym.info))
+        putIntoReg(tos.slots[0], getNullValue(sym.typ.sons[0], sym.info, c.config))
       # XXX We could perform some type checking here.
       for i in 1..<sym.typ.len:
         putIntoReg(tos.slots[i], args[i-1])
 
       result = rawExecute(c, start, tos).regToNode
   else:
-    localError(sym.info,
+    localError(c.config, sym.info,
       "NimScript: attempt to call non-routine: " & sym.name.s)
 
 proc evalStmt*(c: PCtx, n: PNode) =
-  let n = transformExpr(c.module, n)
+  let n = transformExpr(c.graph, c.module, n)
   let start = genStmt(c, n)
   # execute new instructions; this redundant opcEof check saves us lots
   # of allocations in 'execute':
@@ -1618,13 +1643,13 @@ proc evalStmt*(c: PCtx, n: PNode) =
     discard execute(c, start)
 
 proc evalExpr*(c: PCtx, n: PNode): PNode =
-  let n = transformExpr(c.module, n)
+  let n = transformExpr(c.graph, c.module, n)
   let start = genExpr(c, n)
   assert c.code[start].opcode != opcEof
   result = execute(c, start)
 
 proc getGlobalValue*(c: PCtx; s: PSym): PNode =
-  internalAssert s.kind in {skLet, skVar} and sfGlobal in s.flags
+  internalAssert c.config, s.kind in {skLet, skVar} and sfGlobal in s.flags
   result = c.globals.sons[s.position-1]
 
 include vmops
@@ -1635,9 +1660,9 @@ include vmops
 var
   globalCtx*: PCtx
 
-proc setupGlobalCtx(module: PSym; cache: IdentCache) =
+proc setupGlobalCtx(module: PSym; cache: IdentCache; graph: ModuleGraph) =
   if globalCtx.isNil:
-    globalCtx = newCtx(module, cache)
+    globalCtx = newCtx(module, cache, graph)
     registerAdditionalOps(globalCtx)
   else:
     refresh(globalCtx, module)
@@ -1648,31 +1673,31 @@ proc myOpen(graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext =
   #pushStackFrame(c, newStackFrame())
 
   # XXX produce a new 'globals' environment here:
-  setupGlobalCtx(module, cache)
+  setupGlobalCtx(module, cache, graph)
   result = globalCtx
   when hasFFI:
     globalCtx.features = {allowFFI, allowCast}
 
-var oldErrorCount: int
-
 proc myProcess(c: PPassContext, n: PNode): PNode =
+  let c = PCtx(c)
   # don't eval errornous code:
-  if oldErrorCount == msgs.gErrorCounter:
-    evalStmt(PCtx(c), n)
+  if c.oldErrorCount == c.config.errorCounter:
+    evalStmt(c, n)
     result = emptyNode
   else:
     result = n
-  oldErrorCount = msgs.gErrorCounter
+  c.oldErrorCount = c.config.errorCounter
 
 proc myClose(graph: ModuleGraph; c: PPassContext, n: PNode): PNode =
   myProcess(c, n)
 
 const evalPass* = makePass(myOpen, nil, myProcess, myClose)
 
-proc evalConstExprAux(module: PSym; cache: IdentCache; prc: PSym, n: PNode,
+proc evalConstExprAux(module: PSym; cache: IdentCache;
+                      g: ModuleGraph; prc: PSym, n: PNode,
                       mode: TEvalMode): PNode =
-  let n = transformExpr(module, n)
-  setupGlobalCtx(module, cache)
+  let n = transformExpr(g, module, n)
+  setupGlobalCtx(module, cache, g)
   var c = globalCtx
   let oldMode = c.mode
   defer: c.mode = oldMode
@@ -1685,19 +1710,19 @@ proc evalConstExprAux(module: PSym; cache: IdentCache; prc: PSym, n: PNode,
   newSeq(tos.slots, c.prc.maxSlots)
   #for i in 0 ..< c.prc.maxSlots: tos.slots[i] = newNode(nkEmpty)
   result = rawExecute(c, start, tos).regToNode
-  if result.info.line < 0: result.info = n.info
+  if result.info.col < 0: result.info = n.info
 
-proc evalConstExpr*(module: PSym; cache: IdentCache, e: PNode): PNode =
-  result = evalConstExprAux(module, cache, nil, e, emConst)
+proc evalConstExpr*(module: PSym; cache: IdentCache, g: ModuleGraph; e: PNode): PNode =
+  result = evalConstExprAux(module, cache, g, nil, e, emConst)
 
-proc evalStaticExpr*(module: PSym; cache: IdentCache, e: PNode, prc: PSym): PNode =
-  result = evalConstExprAux(module, cache, prc, e, emStaticExpr)
+proc evalStaticExpr*(module: PSym; cache: IdentCache, g: ModuleGraph; e: PNode, prc: PSym): PNode =
+  result = evalConstExprAux(module, cache, g, prc, e, emStaticExpr)
 
-proc evalStaticStmt*(module: PSym; cache: IdentCache, e: PNode, prc: PSym) =
-  discard evalConstExprAux(module, cache, prc, e, emStaticStmt)
+proc evalStaticStmt*(module: PSym; cache: IdentCache, g: ModuleGraph; e: PNode, prc: PSym) =
+  discard evalConstExprAux(module, cache, g, prc, e, emStaticStmt)
 
-proc setupCompileTimeVar*(module: PSym; cache: IdentCache, n: PNode) =
-  discard evalConstExprAux(module, cache, nil, n, emStaticStmt)
+proc setupCompileTimeVar*(module: PSym; cache: IdentCache, g: ModuleGraph; n: PNode) =
+  discard evalConstExprAux(module, cache, g, nil, n, emStaticStmt)
 
 proc setupMacroParam(x: PNode, typ: PType): TFullReg =
   case typ.kind
@@ -1721,24 +1746,26 @@ iterator genericParamsInMacroCall*(macroSym: PSym, call: PNode): (PSym, PNode) =
     let posInCall = macroSym.typ.len + i
     yield (genericParam, call[posInCall])
 
+# to prevent endless recursion in macro instantiation
+const evalMacroLimit = 1000
 var evalMacroCounter: int
 
-proc evalMacroCall*(module: PSym; cache: IdentCache, n, nOrig: PNode,
-                    sym: PSym): PNode =
+proc evalMacroCall*(module: PSym; cache: IdentCache; 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 > 100:
-    globalError(n.info, errTemplateInstantiationTooNested)
+  if evalMacroCounter > evalMacroLimit:
+    globalError(g.config, n.info, "macro instantiation too nested")
 
   # immediate macros can bypass any type and arity checking so we check the
   # arity here too:
   if sym.typ.len > n.safeLen and sym.typ.len > 1:
-    globalError(n.info, "in call '$#' got $#, but expected $# argument(s)" % [
+    globalError(g.config, n.info, "in call '$#' got $#, but expected $# argument(s)" % [
         n.renderTree, $(n.safeLen-1), $(sym.typ.len-1)])
 
-  setupGlobalCtx(module, cache)
+  setupGlobalCtx(module, cache, g)
   var c = globalCtx
-  c.comesFromHeuristic.line = -1
+  c.comesFromHeuristic.line = 0'u16
 
   c.callsite = nOrig
   let start = genProc(c, sym)
@@ -1770,16 +1797,16 @@ proc evalMacroCall*(module: PSym; cache: IdentCache, n, nOrig: PNode,
       else:
         dec(evalMacroCounter)
         c.callsite = nil
-        localError(n.info, "expected " & $gp.len &
+        localError(c.config, n.info, "expected " & $gp.len &
                    " generic parameter(s)")
     elif gp[i].sym.typ.kind in {tyStatic, tyTypeDesc}:
       dec(evalMacroCounter)
       c.callsite = nil
-      globalError(n.info, "static[T] or typedesc nor supported for .immediate macros")
+      globalError(c.config, n.info, "static[T] or typedesc nor supported for .immediate macros")
   # temporary storage:
   #for i in L ..< maxSlots: tos.slots[i] = newNode(nkEmpty)
   result = rawExecute(c, start, tos).regToNode
   if result.info.line < 0: result.info = n.info
-  if cyclicTree(result): globalError(n.info, errCyclicTree)
+  if cyclicTree(result): globalError(c.config, n.info, "macro produced a cyclic tree")
   dec(evalMacroCounter)
   c.callsite = nil
diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim
index 66bc8dfd2..e10a62006 100644
--- a/compiler/vmdef.nim
+++ b/compiler/vmdef.nim
@@ -10,7 +10,7 @@
 ## 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
+import ast, passes, msgs, idents, intsets, options, modulegraphs
 
 const
   byteExcess* = 128 # we use excess-K for immediates
@@ -206,17 +206,20 @@ type
     callbacks*: seq[tuple[key: string, value: VmCallback]]
     errorFlag*: string
     cache*: IdentCache
+    config*: ConfigRef
+    graph*: ModuleGraph
+    oldErrorCount*: int
 
   TPosition* = distinct int
 
   PEvalContext* = PCtx
 
-proc newCtx*(module: PSym; cache: IdentCache): PCtx =
+proc newCtx*(module: PSym; cache: IdentCache; g: ModuleGraph): PCtx =
   PCtx(code: @[], debug: @[],
     globals: newNode(nkStmtListExpr), constants: newNode(nkStmtList), types: @[],
     prc: PProc(blocks: @[]), module: module, loopIterations: MaxLoopIterations,
     comesFromHeuristic: unknownLineInfo(), callbacks: @[], errorFlag: "",
-    cache: cache)
+    cache: cache, config: g.config, graph: g)
 
 proc refresh*(c: PCtx, module: PSym) =
   c.module = module
diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim
index 071cc7706..2c92348a6 100644
--- a/compiler/vmdeps.nim
+++ b/compiler/vmdeps.nim
@@ -9,18 +9,18 @@
 
 import ast, types, msgs, os, streams, options, idents
 
-proc opSlurp*(file: string, info: TLineInfo, module: PSym): string =
+proc opSlurp*(file: string, info: TLineInfo, module: PSym; conf: ConfigRef): string =
   try:
     var filename = parentDir(info.toFullPath) / file
     if not fileExists(filename):
-      filename = file.findFile
+      filename = findFile(conf, file)
     result = readFile(filename)
     # we produce a fake include statement for every slurped filename, so that
     # the module dependencies are accurate:
     appendToModule(module, newNode(nkIncludeStmt, info, @[
       newStrNode(nkStrLit, filename)]))
   except IOError:
-    localError(info, errCannotOpenFile, file)
+    localError(conf, info, "cannot open file: " & file)
     result = ""
 
 proc atomicTypeX(name: string; m: TMagic; t: PType; info: TLineInfo): PNode =
@@ -208,7 +208,12 @@ proc mapTypeToAstX(t: PType; info: TLineInfo;
       result.add mapTypeToAst(t.sons[0], info)
     else:
       result = mapTypeToBracket("ref", mRef, t, info)
-  of tyVar: result = mapTypeToBracket("var", mVar, t, info)
+  of tyVar:
+    if inst:
+      result = newNodeX(nkVarTy)
+      result.add mapTypeToAst(t.sons[0], info)
+    else:
+      result = mapTypeToBracket("var", mVar, t, info)
   of tyLent: result = mapTypeToBracket("lent", mBuiltinType, t, info)
   of tySink: result = mapTypeToBracket("sink", mBuiltinType, t, info)
   of tySequence: result = mapTypeToBracket("seq", mSeq, t, info)
@@ -266,7 +271,7 @@ proc mapTypeToAstX(t: PType; info: TLineInfo;
   of tyOr: result = mapTypeToBracket("or", mOr, t, info)
   of tyNot: result = mapTypeToBracket("not", mNot, t, info)
   of tyAnything: result = atomicType("anything", mNone)
-  of tyInferred: internalAssert false
+  of tyInferred: assert false
   of tyStatic, tyFromExpr:
     if inst:
       if t.n != nil: result = t.n.copyTree
@@ -276,7 +281,7 @@ proc mapTypeToAstX(t: PType; info: TLineInfo;
       result.add atomicType("static", mNone)
       if t.n != nil:
         result.add t.n.copyTree
-  of tyUnused, tyOptAsRef: internalError("mapTypeToAstX")
+  of tyUnused, tyOptAsRef: assert(false, "mapTypeToAstX")
 
 proc opMapTypeToAst*(t: PType; info: TLineInfo): PNode =
   result = mapTypeToAstX(t, info, false, true)
diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim
index c3eaf8946..7ac3b5cf7 100644
--- a/compiler/vmgen.nim
+++ b/compiler/vmgen.nim
@@ -120,7 +120,7 @@ proc gABI(c: PCtx; n: PNode; opc: TOpcode; a, b: TRegister; imm: BiggestInt) =
     c.code.add(ins)
     c.debug.add(n.info)
   else:
-    localError(n.info, errGenerated,
+    localError(c.config, n.info,
       "VM: immediate value does not fit into an int8")
 
 proc gABx(c: PCtx; n: PNode; opc: TOpcode; a: TRegister = 0; bx: int) =
@@ -137,7 +137,7 @@ proc gABx(c: PCtx; n: PNode; opc: TOpcode; a: TRegister = 0; bx: int) =
     c.code.add(ins)
     c.debug.add(n.info)
   else:
-    localError(n.info, errGenerated,
+    localError(c.config, n.info,
       "VM: immediate value does not fit into an int16")
 
 proc xjmp(c: PCtx; n: PNode; opc: TOpcode; a: TRegister = 0): TPosition =
@@ -151,7 +151,7 @@ proc genLabel(c: PCtx): TPosition =
 
 proc jmpBack(c: PCtx, n: PNode, p = TPosition(0)) =
   let dist = p.int - c.code.len
-  internalAssert(-0x7fff < dist and dist < 0x7fff)
+  internalAssert(c.config, -0x7fff < dist and dist < 0x7fff)
   gABx(c, n, opcJmpBack, 0, dist)
 
 proc patch(c: PCtx, p: TPosition) =
@@ -159,7 +159,7 @@ proc patch(c: PCtx, p: TPosition) =
   let p = p.int
   let diff = c.code.len - p
   #c.jumpTargets.incl(c.code.len)
-  internalAssert(-0x7fff < diff and diff < 0x7fff)
+  internalAssert(c.config, -0x7fff < diff and diff < 0x7fff)
   let oldInstr = c.code[p]
   # opcode and regA stay the same:
   c.code[p] = ((oldInstr.uint32 and 0xffff'u32).uint32 or
@@ -201,7 +201,7 @@ proc getTemp(cc: PCtx; tt: PType): TRegister =
         c.slots[i] = (inUse: true, kind: k)
         return TRegister(i)
   if c.maxSlots >= high(TRegister):
-    globalError(cc.bestEffort, "VM problem: too many registers required")
+    globalError(cc.config, cc.bestEffort, "VM problem: too many registers required")
   result = TRegister(c.maxSlots)
   c.slots[c.maxSlots] = (inUse: true, kind: k)
   inc c.maxSlots
@@ -223,7 +223,7 @@ proc getTempRange(cc: PCtx; n: int; kind: TSlotKind): TRegister =
           for k in result .. result+n-1: c.slots[k] = (inUse: true, kind: kind)
           return
   if c.maxSlots+n >= high(TRegister):
-    globalError(cc.bestEffort, "VM problem: too many registers required")
+    globalError(cc.config, cc.bestEffort, "VM problem: too many registers required")
   result = TRegister(c.maxSlots)
   inc c.maxSlots, n
   for k in result .. result+n-1: c.slots[k] = (inUse: true, kind: kind)
@@ -251,7 +251,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {})
 proc gen(c: PCtx; n: PNode; dest: TRegister; flags: TGenFlags = {}) =
   var d: TDest = dest
   gen(c, n, d, flags)
-  #internalAssert d == dest # issue #7407
+  #internalAssert c.config, d == dest # issue #7407
 
 proc gen(c: PCtx; n: PNode; flags: TGenFlags = {}) =
   var tmp: TDest = -1
@@ -261,7 +261,7 @@ proc gen(c: PCtx; n: PNode; flags: TGenFlags = {}) =
 proc genx(c: PCtx; n: PNode; flags: TGenFlags = {}): TRegister =
   var tmp: TDest = -1
   gen(c, n, tmp, flags)
-  #internalAssert tmp >= 0 # 'nim check' does not like this internalAssert.
+  #internalAssert c.config, tmp >= 0 # 'nim check' does not like this internalAssert.
   if tmp >= 0:
     result = TRegister(tmp)
 
@@ -320,7 +320,7 @@ proc genBreak(c: PCtx; n: PNode) =
       if c.prc.blocks[i].label == n.sons[0].sym:
         c.prc.blocks[i].fixups.add L1
         return
-    globalError(n.info, errGenerated, "VM problem: cannot find 'break' target")
+    globalError(c.config, n.info, "VM problem: cannot find 'break' target")
   else:
     c.prc.blocks[c.prc.blocks.high].fixups.add L1
 
@@ -378,7 +378,7 @@ proc rawGenLiteral(c: PCtx; n: PNode): int =
   #assert(n.kind != nkCall)
   n.flags.incl nfAllConst
   c.constants.add n.canonValue
-  internalAssert result < 0x7fff
+  internalAssert c.config, result < 0x7fff
 
 proc sameConstant*(a, b: PNode): bool =
   result = false
@@ -405,10 +405,10 @@ proc genLiteral(c: PCtx; n: PNode): int =
     if sameConstant(c.constants[i], n): return i
   result = rawGenLiteral(c, n)
 
-proc unused(n: PNode; x: TDest) {.inline.} =
+proc unused(c: PCtx; n: PNode; x: TDest) {.inline.} =
   if x >= 0:
     #debug(n)
-    globalError(n.info, "not unused")
+    globalError(c.config, n.info, "not unused")
 
 proc genCase(c: PCtx; n: PNode; dest: var TDest) =
   #  if (!expr1) goto L1;
@@ -424,7 +424,7 @@ proc genCase(c: PCtx; n: PNode; dest: var TDest) =
   if not isEmptyType(n.typ):
     if dest < 0: dest = getTemp(c, n.typ)
   else:
-    unused(n, dest)
+    unused(c, n, dest)
   var endings: seq[TPosition] = @[]
   withTemp(tmp, n.sons[0].typ):
     c.gen(n.sons[0], tmp)
@@ -451,7 +451,7 @@ proc genType(c: PCtx; typ: PType): int =
     if sameType(t, typ): return i
   result = c.types.len
   c.types.add(typ)
-  internalAssert(result <= 0x7fff)
+  internalAssert(c.config, result <= 0x7fff)
 
 proc genTry(c: PCtx; n: PNode; dest: var TDest) =
   if dest < 0 and not isEmptyType(n.typ): dest = getTemp(c, n.typ)
@@ -525,7 +525,7 @@ proc genCall(c: PCtx; n: PNode; dest: var TDest) =
     var r: TRegister = x+i
     c.gen(n.sons[i], r)
     if i >= fntyp.len:
-      internalAssert tfVarargs in fntyp.flags
+      internalAssert c.config, tfVarargs in fntyp.flags
       c.gABx(n, opcSetType, r, c.genType(n.sons[i].typ))
   if dest < 0:
     c.gABC(n, opcIndCall, 0, x, n.len)
@@ -540,12 +540,12 @@ proc needsAsgnPatch(n: PNode): bool =
   n.kind in {nkBracketExpr, nkDotExpr, nkCheckedFieldExpr,
              nkDerefExpr, nkHiddenDeref} or (n.kind == nkSym and n.sym.isGlobal)
 
-proc genField(n: PNode): TRegister =
+proc genField(c: PCtx; n: PNode): TRegister =
   if n.kind != nkSym or n.sym.kind != skField:
-    globalError(n.info, "no field symbol")
+    globalError(c.config, n.info, "no field symbol")
   let s = n.sym
   if s.position > high(result):
-    globalError(n.info,
+    globalError(c.config, n.info,
         "too large offset! cannot generate code for: " & s.name.s)
   result = s.position
 
@@ -572,7 +572,7 @@ proc genAsgnPatch(c: PCtx; le: PNode, value: TRegister) =
     # XXX field checks here
     let left = if le.kind == nkDotExpr: le else: le.sons[0]
     let dest = c.genx(left.sons[0], {gfAddrOf, gfFieldAccess})
-    let idx = genField(left.sons[1])
+    let idx = genField(c, left.sons[1])
     c.gABC(left, opcWrObj, dest, idx, value)
     c.freeTemp(dest)
   of nkDerefExpr, nkHiddenDeref:
@@ -779,7 +779,7 @@ proc genIntCast(c: PCtx; n: PNode; dest: var TDest) =
     let tmp3 = c.getTemp(n.sons[1].typ)
     if dest < 0: dest = c.getTemp(n[0].typ)
     proc mkIntLit(ival: int): int =
-      result = genLiteral(c, newIntTypeNode(nkIntLit, ival, getSysType(tyInt)))
+      result = genLiteral(c, newIntTypeNode(nkIntLit, ival, getSysType(c.graph, n.info, tyInt)))
     if src.kind in unsignedIntegers and dst.kind in signedIntegers:
       # cast unsigned to signed integer of same size
       # signedVal = (unsignedVal xor offset) -% offset
@@ -790,7 +790,7 @@ proc genIntCast(c: PCtx; n: PNode; dest: var TDest) =
     elif src.kind in signedIntegers and dst.kind in unsignedIntegers:
       # cast signed to unsigned integer of same size
       # unsignedVal = (offset +% signedVal +% 1) and offset
-      let offset = (1 shl (src_size * 8))  - 1
+      let offset = (1 shl (src_size * 8)) - 1
       c.gABx(n, opcLdConst, tmp2, mkIntLit(offset))
       c.gABx(n, opcLdConst, dest, mkIntLit(offset+1))
       c.gABC(n, opcAddu, tmp3, tmp, dest)
@@ -802,7 +802,7 @@ proc genIntCast(c: PCtx; n: PNode; dest: var TDest) =
     c.freeTemp(tmp2)
     c.freeTemp(tmp3)
   else:
-    globalError(n.info, errGenerated, "VM is only allowed to 'cast' between integers of same size")
+    globalError(c.config, n.info, "VM is only allowed to 'cast' between integers of same size")
 
 proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
   case m
@@ -818,7 +818,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
   of mSucc, mAddI:
     c.genAddSubInt(n, dest, opcAddInt)
   of mInc, mDec:
-    unused(n, dest)
+    unused(c, n, dest)
     let opc = if m == mInc: opcAddInt else: opcSubInt
     let d = c.genx(n.sons[1])
     if n.sons[2].isInt8Lit:
@@ -832,10 +832,10 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
     c.freeTemp(d)
   of mOrd, mChr, mArrToSeq: c.gen(n.sons[1], dest)
   of mNew, mNewFinalize:
-    unused(n, dest)
+    unused(c, n, dest)
     c.genNew(n)
   of mNewSeq:
-    unused(n, dest)
+    unused(c, n, dest)
     c.genNewSeq(n)
   of mNewSeqOfCap: c.genNewSeqOfCap(n, dest)
   of mNewString:
@@ -855,7 +855,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
   of mLengthStr, mXLenStr:
     genUnaryABI(c, n, dest, opcLenStr)
   of mIncl, mExcl:
-    unused(n, dest)
+    unused(c, n, dest)
     var d = c.genx(n.sons[1])
     var tmp = c.genx(n.sons[2])
     c.genSetType(n.sons[1], d)
@@ -952,19 +952,19 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
   of mInSet: genBinarySet(c, n, dest, opcContainsSet)
   of mRepr: genUnaryABC(c, n, dest, opcRepr)
   of mExit:
-    unused(n, dest)
+    unused(c, n, dest)
     var tmp = c.genx(n.sons[1])
     c.gABC(n, opcQuit, tmp)
     c.freeTemp(tmp)
   of mSetLengthStr, mSetLengthSeq:
-    unused(n, dest)
+    unused(c, n, dest)
     var d = c.genx(n.sons[1])
     var tmp = c.genx(n.sons[2])
     c.gABC(n, if m == mSetLengthStr: opcSetLenStr else: opcSetLenSeq, d, tmp)
     c.genAsgnPatch(n.sons[1], d)
     c.freeTemp(tmp)
   of mSwap:
-    unused(n, dest)
+    unused(c, n, dest)
     c.gen(lowerSwap(n, if c.prc == nil: c.module else: c.prc.sym))
   of mIsNil: genUnaryABC(c, n, dest, opcIsNil)
   of mCopyStr:
@@ -996,7 +996,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
     # skip 'nkHiddenAddr':
     let d2AsNode = n.sons[2].sons[0]
     if needsAsgnPatch(d2AsNode):
-      d2 = c.getTemp(getSysType(tyFloat))
+      d2 = c.getTemp(getSysType(c.graph, n.info, tyFloat))
     else:
       d2 = c.genx(d2AsNode)
     var
@@ -1009,13 +1009,13 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
     c.genAsgnPatch(d2AsNode, d2)
     c.freeTemp(d2)
   of mReset:
-    unused(n, dest)
+    unused(c, n, dest)
     var d = c.genx(n.sons[1])
     c.gABC(n, opcReset, d)
   of mOf, mIs:
     if dest < 0: dest = c.getTemp(n.typ)
     var tmp = c.genx(n.sons[1])
-    var idx = c.getTemp(getSysType(tyInt))
+    var idx = c.getTemp(getSysType(c.graph, n.info, tyInt))
     var typ = n.sons[2].typ
     if m == mOf: typ = typ.skipTypes(abstractPtrs-{tyTypeDesc})
     c.gABx(n, opcLdImmInt, idx, c.genType(typ))
@@ -1023,7 +1023,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
     c.freeTemp(tmp)
     c.freeTemp(idx)
   of mSizeOf:
-    globalError(n.info, errCannotInterpretNodeX, renderTree(n))
+    globalError(c.config, n.info, "cannot run in the VM: " & renderTree(n))
   of mHigh:
     if dest < 0: dest = c.getTemp(n.typ)
     let tmp = c.genx(n.sons[1])
@@ -1034,23 +1034,23 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
       c.gABI(n, opcLenSeq, dest, tmp, 1)
     c.freeTemp(tmp)
   of mEcho:
-    unused(n, dest)
+    unused(c, n, dest)
     let n = n[1].skipConv
     let x = c.getTempRange(n.len, slotTempUnknown)
-    internalAssert n.kind == nkBracket
+    internalAssert c.config, n.kind == nkBracket
     for i in 0..<n.len:
       var r: TRegister = x+i
       c.gen(n.sons[i], r)
     c.gABC(n, opcEcho, x, n.len)
     c.freeTempRange(x, n.len)
   of mAppendStrCh:
-    unused(n, dest)
+    unused(c, n, dest)
     genBinaryStmtVar(c, n, opcAddStrCh)
   of mAppendStrStr:
-    unused(n, dest)
+    unused(c, n, dest)
     genBinaryStmtVar(c, n, opcAddStrStr)
   of mAppendSeqElem:
-    unused(n, dest)
+    unused(c, n, dest)
     genBinaryStmtVar(c, n, opcAddSeqElem)
   of mParseExprToAst:
     genUnaryABC(c, n, dest, opcParseExprToAst)
@@ -1068,7 +1068,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
   of mGetImpl: genUnaryABC(c, n, dest, opcGetImpl)
   of mNChild: genBinaryABC(c, n, dest, opcNChild)
   of mNSetChild, mNDel:
-    unused(n, dest)
+    unused(c, n, dest)
     var
       tmp1 = c.genx(n.sons[1])
       tmp2 = c.genx(n.sons[2])
@@ -1098,22 +1098,22 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
     #genUnaryABC(c, n, dest, opcNGetType)
   of mNStrVal: genUnaryABC(c, n, dest, opcNStrVal)
   of mNSetIntVal:
-    unused(n, dest)
+    unused(c, n, dest)
     genBinaryStmt(c, n, opcNSetIntVal)
   of mNSetFloatVal:
-    unused(n, dest)
+    unused(c, n, dest)
     genBinaryStmt(c, n, opcNSetFloatVal)
   of mNSetSymbol:
-    unused(n, dest)
+    unused(c, n, dest)
     genBinaryStmt(c, n, opcNSetSymbol)
   of mNSetIdent:
-    unused(n, dest)
+    unused(c, n, dest)
     genBinaryStmt(c, n, opcNSetIdent)
   of mNSetType:
-    unused(n, dest)
+    unused(c, n, dest)
     genBinaryStmt(c, n, opcNSetType)
   of mNSetStrVal:
-    unused(n, dest)
+    unused(c, n, dest)
     genBinaryStmt(c, n, opcNSetStrVal)
   of mNNewNimNode: genBinaryABC(c, n, dest, opcNNewNimNode)
   of mNCopyNimNode: genUnaryABC(c, n, dest, opcNCopyNimNode)
@@ -1124,7 +1124,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
       if dest < 0: dest = c.getTemp(n.typ)
       c.gABx(n, opcNBindSym, dest, idx)
     else:
-      localError(n.info, "invalid bindSym usage")
+      localError(c.config, n.info, "invalid bindSym usage")
   of mStrToIdent: genUnaryABC(c, n, dest, opcStrToIdent)
   of mEqIdent: genBinaryABC(c, n, dest, opcEqIdent)
   of mEqNimrodNode: genBinaryABC(c, n, dest, opcEqNimrodNode)
@@ -1138,12 +1138,12 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
     of "getColumn":
       genUnaryABC(c, n, dest, opcNGetColumn)
     else:
-      internalAssert false
+      internalAssert c.config, false
   of mNHint:
-    unused(n, dest)
+    unused(c, n, dest)
     genUnaryStmt(c, n, opcNHint)
   of mNWarning:
-    unused(n, dest)
+    unused(c, n, dest)
     genUnaryStmt(c, n, opcNWarning)
   of mNError:
     if n.len <= 1:
@@ -1151,7 +1151,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
       c.gABC(n, opcQueryErrorFlag, dest)
     else:
       # setter
-      unused(n, dest)
+      unused(c, n, dest)
       genBinaryStmt(c, n, opcNError)
   of mNCallSite:
     if dest < 0: dest = c.getTemp(n.typ)
@@ -1162,7 +1162,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
     c.genCall(n, dest)
   of mExpandToAst:
     if n.len != 2:
-      globalError(n.info, errGenerated, "expandToAst requires 1 argument")
+      globalError(c.config, n.info, "expandToAst requires 1 argument")
     let arg = n.sons[1]
     if arg.kind in nkCallKinds:
       #if arg[0].kind != nkSym or arg[0].sym.kind notin {skTemplate, skMacro}:
@@ -1172,12 +1172,12 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
       # do not call clearDest(n, dest) here as getAst has a meta-type as such
       # produces a value
     else:
-      globalError(n.info, "expandToAst requires a call expression")
+      globalError(c.config, n.info, "expandToAst requires a call expression")
   of mRunnableExamples:
     discard "just ignore any call to runnableExamples"
   else:
     # mGCref, mGCunref,
-    globalError(n.info, "cannot generate code for: " & $m)
+    globalError(c.config, n.info, "cannot generate code for: " & $m)
 
 proc genMarshalLoad(c: PCtx, n: PNode, dest: var TDest) =
   ## Signature: proc to*[T](data: string): T
@@ -1306,14 +1306,14 @@ proc setSlot(c: PCtx; v: PSym) =
   if v.position == 0:
     if c.prc.maxSlots == 0: c.prc.maxSlots = 1
     if c.prc.maxSlots >= high(TRegister):
-      globalError(v.info, "cannot generate code; too many registers required")
+      globalError(c.config, v.info, "cannot generate code; too many registers required")
     v.position = c.prc.maxSlots
     c.prc.slots[v.position] = (inUse: true,
         kind: if v.kind == skLet: slotFixedLet else: slotFixedVar)
     inc c.prc.maxSlots
 
-proc cannotEval(n: PNode) {.noinline.} =
-  globalError(n.info, errGenerated, "cannot evaluate at compile time: " &
+proc cannotEval(c: PCtx; n: PNode) {.noinline.} =
+  globalError(c.config, n.info, "cannot evaluate at compile time: " &
     n.renderTree)
 
 proc isOwnedBy(a, b: PSym): bool =
@@ -1333,10 +1333,10 @@ proc checkCanEval(c: PCtx; n: PNode) =
   if {sfCompileTime, sfGlobal} <= s.flags: return
   if s.kind in {skVar, skTemp, skLet, skParam, skResult} and
       not s.isOwnedBy(c.prc.sym) and s.owner != c.module and c.mode != emRepl:
-    cannotEval(n)
+    cannotEval(c, n)
   elif s.kind in {skProc, skFunc, skConverter, skMethod,
                   skIterator} and sfForward in s.flags:
-    cannotEval(n)
+    cannotEval(c, n)
 
 proc isTemp(c: PCtx; dest: TDest): bool =
   result = dest >= 0 and c.prc.slots[dest].kind >= slotTempUnknown
@@ -1378,7 +1378,7 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) =
     # XXX field checks here
     let left = if le.kind == nkDotExpr: le else: le.sons[0]
     let dest = c.genx(left.sons[0], {gfAddrOf, gfFieldAccess})
-    let idx = genField(left.sons[1])
+    let idx = genField(c, left.sons[1])
     let tmp = c.genx(ri)
     c.preventFalseAlias(left, opcWrObj, dest, idx, tmp)
     c.freeTemp(tmp)
@@ -1398,7 +1398,7 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) =
         c.freeTemp(val)
     else:
       if s.kind == skForVar: c.setSlot s
-      internalAssert s.position > 0 or (s.position == 0 and
+      internalAssert c.config, s.position > 0 or (s.position == 0 and
                                         s.kind in {skParam,skResult})
       var dest: TRegister = s.position + ord(s.kind == skParam)
       assert le.typ != nil
@@ -1424,15 +1424,15 @@ proc importcSym(c: PCtx; info: TLineInfo; s: PSym) =
       c.globals.add(importcSymbol(s))
       s.position = c.globals.len
     else:
-      localError(info, errGenerated, "VM is not allowed to 'importc'")
+      localError(c.config, info, "VM is not allowed to 'importc'")
   else:
-    localError(info, errGenerated,
+    localError(c.config, info,
                "cannot 'importc' variable at compile time")
 
-proc getNullValue*(typ: PType, info: TLineInfo): PNode
+proc getNullValue*(typ: PType, info: TLineInfo; conf: ConfigRef): PNode
 
 proc genGlobalInit(c: PCtx; n: PNode; s: PSym) =
-  c.globals.add(getNullValue(s.typ, n.info))
+  c.globals.add(getNullValue(s.typ, n.info, c.config))
   s.position = c.globals.len
   # This is rather hard to support, due to the laziness of the VM code
   # generator. See tests/compile/tmacro2 for why this is necessary:
@@ -1451,7 +1451,7 @@ proc genRdVar(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
     if sfCompileTime in s.flags or c.mode == emRepl:
       discard
     elif s.position == 0:
-      cannotEval(n)
+      cannotEval(c, n)
     if s.position == 0:
       if sfImportc in s.flags: c.importcSym(n.info, s)
       else: genGlobalInit(c, n, s)
@@ -1472,13 +1472,13 @@ proc genRdVar(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
                           s.kind in {skParam,skResult}):
       if dest < 0:
         dest = s.position + ord(s.kind == skParam)
-        internalAssert(c.prc.slots[dest].kind < slotSomeTemp)
+        internalAssert(c.config, c.prc.slots[dest].kind < slotSomeTemp)
       else:
         # we need to generate an assignment:
         genAsgn(c, dest, n, c.prc.slots[dest].kind >= slotSomeTemp)
     else:
       # see tests/t99bott for an example that triggers it:
-      cannotEval(n)
+      cannotEval(c, n)
 
 template needsRegLoad(): untyped =
   gfAddrOf notin flags and fitsRegister(n.typ.skipTypes({tyVar, tyLent}))
@@ -1502,7 +1502,7 @@ proc genArrAccess2(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode;
 
 proc genObjAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
   let a = c.genx(n.sons[0], flags)
-  let b = genField(n.sons[1])
+  let b = genField(c, n.sons[1])
   if dest < 0: dest = c.getTemp(n.typ)
   if needsRegLoad():
     var cc = c.getTemp(n.typ)
@@ -1526,22 +1526,22 @@ proc genArrAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
   else:
     genArrAccess2(c, n, dest, opcLdArr, flags)
 
-proc getNullValueAux(obj: PNode, result: PNode) =
+proc getNullValueAux(obj: PNode, result: PNode; conf: ConfigRef) =
   case obj.kind
   of nkRecList:
-    for i in countup(0, sonsLen(obj) - 1): getNullValueAux(obj.sons[i], result)
+    for i in countup(0, sonsLen(obj) - 1): getNullValueAux(obj.sons[i], result, conf)
   of nkRecCase:
-    getNullValueAux(obj.sons[0], result)
+    getNullValueAux(obj.sons[0], result, conf)
     for i in countup(1, sonsLen(obj) - 1):
-      getNullValueAux(lastSon(obj.sons[i]), result)
+      getNullValueAux(lastSon(obj.sons[i]), result, conf)
   of nkSym:
     let field = newNodeI(nkExprColonExpr, result.info)
     field.add(obj)
-    field.add(getNullValue(obj.sym.typ, result.info))
+    field.add(getNullValue(obj.sym.typ, result.info, conf))
     addSon(result, field)
-  else: globalError(result.info, "cannot create null element for: " & $obj)
+  else: globalError(conf, result.info, "cannot create null element for: " & $obj)
 
-proc getNullValue(typ: PType, info: TLineInfo): PNode =
+proc getNullValue(typ: PType, info: TLineInfo; conf: ConfigRef): PNode =
   var t = skipTypes(typ, abstractRange-{tyTypeDesc})
   result = emptyNode
   case t.kind
@@ -1553,7 +1553,8 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode =
     result = newNodeIT(nkFloatLit, info, t)
   of tyCString, tyString:
     result = newNodeIT(nkStrLit, info, t)
-  of tyVar, tyLent, tyPointer, tyPtr, tySequence, tyExpr,
+    result.strVal = ""
+  of tyVar, tyLent, tyPointer, tyPtr, tyExpr,
      tyStmt, tyTypeDesc, tyStatic, tyRef, tyNil:
     result = newNodeIT(nkNilLit, info, t)
   of tyProc:
@@ -1569,23 +1570,25 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode =
     # initialize inherited fields:
     var base = t.sons[0]
     while base != nil:
-      getNullValueAux(skipTypes(base, skipPtrs).n, result)
+      getNullValueAux(skipTypes(base, skipPtrs).n, result, conf)
       base = base.sons[0]
-    getNullValueAux(t.n, result)
+    getNullValueAux(t.n, result, conf)
   of tyArray:
     result = newNodeIT(nkBracket, info, t)
     for i in countup(0, int(lengthOrd(t)) - 1):
-      addSon(result, getNullValue(elemType(t), info))
+      addSon(result, getNullValue(elemType(t), info, conf))
   of tyTuple:
     result = newNodeIT(nkTupleConstr, info, t)
     for i in countup(0, sonsLen(t) - 1):
-      addSon(result, getNullValue(t.sons[i], info))
+      addSon(result, getNullValue(t.sons[i], info, conf))
   of tySet:
     result = newNodeIT(nkCurly, info, t)
   of tyOpt:
     result = newNodeIT(nkNilLit, info, t)
+  of tySequence:
+    result = newNodeIT(nkBracket, info, t)
   else:
-    globalError(info, "cannot create null element for: " & $t.kind)
+    globalError(conf, info, "cannot create null element for: " & $t.kind)
 
 proc ldNullOpcode(t: PType): TOpcode =
   assert t != nil
@@ -1599,7 +1602,7 @@ proc genVarSection(c: PCtx; n: PNode) =
       for i in 0 .. a.len-3:
         if not a[i].sym.isGlobal: setSlot(c, a[i].sym)
         checkCanEval(c, a[i])
-      c.gen(lowerTupleUnpacking(a, c.getOwner))
+      c.gen(lowerTupleUnpacking(c.graph, a, c.getOwner))
     elif a.sons[0].kind == nkSym:
       let s = a.sons[0].sym
       checkCanEval(c, a.sons[0])
@@ -1607,7 +1610,7 @@ proc genVarSection(c: PCtx; n: PNode) =
         if s.position == 0:
           if sfImportc in s.flags: c.importcSym(a.info, s)
           else:
-            let sa = getNullValue(s.typ, a.info)
+            let sa = getNullValue(s.typ, a.info, c.config)
             #if s.ast.isNil: getNullValue(s.typ, a.info)
             #else: canonValue(s.ast)
             assert sa.kind != nkCall
@@ -1649,7 +1652,7 @@ proc genArrayConstr(c: PCtx, n: PNode, dest: var TDest) =
   if dest < 0: dest = c.getTemp(n.typ)
   c.gABx(n, opcLdNull, dest, c.genType(n.typ))
 
-  let intType = getSysType(tyInt)
+  let intType = getSysType(c.graph, n.info, tyInt)
   let seqType = n.typ.skipTypes(abstractVar-{tyTypeDesc})
   if seqType.kind == tySequence:
     var tmp = c.getTemp(intType)
@@ -1693,13 +1696,13 @@ proc genObjConstr(c: PCtx, n: PNode, dest: var TDest) =
   for i in 1..<n.len:
     let it = n.sons[i]
     if it.kind == nkExprColonExpr and it.sons[0].kind == nkSym:
-      let idx = genField(it.sons[0])
+      let idx = genField(c, it.sons[0])
       let tmp = c.genx(it.sons[1])
       c.preventFalseAlias(it.sons[1], whichAsgnOpc(it.sons[1], opcWrObj),
                           dest, idx, tmp)
       c.freeTemp(tmp)
     else:
-      globalError(n.info, "invalid object constructor")
+      globalError(c.config, n.info, "invalid object constructor")
 
 proc genTupleConstr(c: PCtx, n: PNode, dest: var TDest) =
   if dest < 0: dest = c.getTemp(n.typ)
@@ -1708,7 +1711,7 @@ proc genTupleConstr(c: PCtx, n: PNode, dest: var TDest) =
   for i in 0..<n.len:
     let it = n.sons[i]
     if it.kind == nkExprColonExpr:
-      let idx = genField(it.sons[0])
+      let idx = genField(c, it.sons[0])
       let tmp = c.genx(it.sons[1])
       c.preventFalseAlias(it.sons[1], whichAsgnOpc(it.sons[1], opcWrObj),
                           dest, idx, tmp)
@@ -1783,9 +1786,9 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
       if c.prc.sym != nil and c.prc.sym.kind == skMacro:
         genRdVar(c, n, dest, flags)
       else:
-        globalError(n.info, errGenerated, "cannot generate code for: " & s.name.s)
+        globalError(c.config, n.info, "cannot generate code for: " & s.name.s)
     else:
-      globalError(n.info, errGenerated, "cannot generate code for: " & s.name.s)
+      globalError(c.config, n.info, "cannot generate code for: " & s.name.s)
   of nkCallKinds:
     if n.sons[0].kind == nkSym:
       let s = n.sons[0].sym
@@ -1809,10 +1812,10 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
       genLit(c, n, dest)
   of nkUIntLit..pred(nkNilLit): genLit(c, n, dest)
   of nkNilLit:
-    if not n.typ.isEmptyType: genLit(c, getNullValue(n.typ, n.info), dest)
-    else: unused(n, dest)
+    if not n.typ.isEmptyType: genLit(c, getNullValue(n.typ, n.info, c.config), dest)
+    else: unused(c, n, dest)
   of nkAsgn, nkFastAsgn:
-    unused(n, dest)
+    unused(c, n, dest)
     genAsgn(c, n.sons[0], n.sons[1], n.kind == nkAsgn)
   of nkDotExpr: genObjAccess(c, n, dest, flags)
   of nkCheckedFieldExpr: genCheckedObjAccess(c, n, dest, flags)
@@ -1825,20 +1828,20 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
     gen(c, n.sons[0].sons[1], dest)
   of nkCaseStmt: genCase(c, n, dest)
   of nkWhileStmt:
-    unused(n, dest)
+    unused(c, n, dest)
     genWhile(c, n)
   of nkBlockExpr, nkBlockStmt: genBlock(c, n, dest)
   of nkReturnStmt:
-    unused(n, dest)
+    unused(c, n, dest)
     genReturn(c, n)
   of nkRaiseStmt:
     genRaise(c, n)
   of nkBreakStmt:
-    unused(n, dest)
+    unused(c, n, dest)
     genBreak(c, n)
   of nkTryStmt: genTry(c, n, dest)
   of nkStmtList:
-    #unused(n, dest)
+    #unused(c, n, dest)
     # XXX Fix this bug properly, lexim triggers it
     for x in n: gen(c, x)
   of nkStmtListExpr:
@@ -1848,17 +1851,17 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
   of nkPragmaBlock:
     gen(c, n.lastSon, dest, flags)
   of nkDiscardStmt:
-    unused(n, dest)
+    unused(c, n, dest)
     gen(c, n.sons[0])
   of nkHiddenStdConv, nkHiddenSubConv, nkConv:
     genConv(c, n, n.sons[1], dest)
   of nkObjDownConv:
     genConv(c, n, n.sons[0], dest)
   of nkVarSection, nkLetSection:
-    unused(n, dest)
+    unused(c, n, dest)
     genVarSection(c, n)
   of declarativeDefs, nkMacroDef:
-    unused(n, dest)
+    unused(c, n, dest)
   of nkLambdaKinds:
     #let s = n.sons[namePos].sym
     #discard genProc(c, s)
@@ -1878,7 +1881,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
       dest = tmp0
   of nkEmpty, nkCommentStmt, nkTypeSection, nkConstSection, nkPragma,
      nkTemplateDef, nkIncludeStmt, nkImportStmt, nkFromStmt:
-    unused(n, dest)
+    unused(c, n, dest)
   of nkStringToCString, nkCStringToString:
     gen(c, n.sons[0], dest)
   of nkBracket: genArrayConstr(c, n, dest)
@@ -1895,7 +1898,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
   of nkComesFrom:
     discard "XXX to implement for better stack traces"
   else:
-    globalError(n.info, errGenerated, "cannot generate VM code for " & $n)
+    globalError(c.config, n.info, "cannot generate VM code for " & $n)
 
 proc removeLastEof(c: PCtx) =
   let last = c.code.len-1
@@ -1912,7 +1915,7 @@ proc genStmt*(c: PCtx; n: PNode): int =
   c.gen(n, d)
   c.gABC(n, opcEof)
   if d >= 0:
-    globalError(n.info, errGenerated, "VM problem: dest register is set")
+    globalError(c.config, n.info, "VM problem: dest register is set")
 
 proc genExpr*(c: PCtx; n: PNode, requiresValue = true): int =
   c.removeLastEof
@@ -1921,7 +1924,7 @@ proc genExpr*(c: PCtx; n: PNode, requiresValue = true): int =
   c.gen(n, d)
   if d < 0:
     if requiresValue:
-      globalError(n.info, errGenerated, "VM problem: dest register is not set")
+      globalError(c.config, n.info, "VM problem: dest register is not set")
     d = 0
   c.gABC(n, opcEof, d)
 
@@ -1936,7 +1939,7 @@ proc genParams(c: PCtx; params: PNode) =
   c.prc.maxSlots = max(params.len, 1)
 
 proc finalJumpTarget(c: PCtx; pc, diff: int) =
-  internalAssert(-0x7fff < diff and diff < 0x7fff)
+  internalAssert(c.config, -0x7fff < diff and diff < 0x7fff)
   let oldInstr = c.code[pc]
   # opcode and regA stay the same:
   c.code[pc] = ((oldInstr.uint32 and 0xffff'u32).uint32 or
diff --git a/compiler/vmmarshal.nim b/compiler/vmmarshal.nim
index d76909443..f38be7c29 100644
--- a/compiler/vmmarshal.nim
+++ b/compiler/vmmarshal.nim
@@ -9,7 +9,8 @@
 
 ## Implements marshaling for the VM.
 
-import streams, json, intsets, tables, ast, astalgo, idents, types, msgs
+import streams, json, intsets, tables, ast, astalgo, idents, types, msgs,
+  options
 
 proc ptrToInt(x: PNode): int {.inline.} =
   result = cast[int](x) # don't skip alignment
@@ -28,37 +29,38 @@ proc getField(n: PNode; position: int): PSym =
       of nkOfBranch, nkElse:
         result = getField(lastSon(n.sons[i]), position)
         if result != nil: return
-      else: internalError(n.info, "getField(record case branch)")
+      else: discard
   of nkSym:
     if n.sym.position == position: result = n.sym
   else: discard
 
-proc storeAny(s: var string; t: PType; a: PNode; stored: var IntSet)
+proc storeAny(s: var string; t: PType; a: PNode; stored: var IntSet; conf: ConfigRef)
 
-proc storeObj(s: var string; typ: PType; x: PNode; stored: var IntSet) =
-  internalAssert x.kind == nkObjConstr
+proc storeObj(s: var string; typ: PType; x: PNode; stored: var IntSet; conf: ConfigRef) =
+  assert x.kind == nkObjConstr
   let start = 1
   for i in countup(start, sonsLen(x) - 1):
     if i > start: s.add(", ")
     var it = x.sons[i]
     if it.kind == nkExprColonExpr:
-      internalAssert it.sons[0].kind == nkSym
-      let field = it.sons[0].sym
-      s.add(escapeJson(field.name.s))
-      s.add(": ")
-      storeAny(s, field.typ, it.sons[1], stored)
+      if it.sons[0].kind == nkSym:
+        let field = it.sons[0].sym
+        s.add(escapeJson(field.name.s))
+        s.add(": ")
+        storeAny(s, field.typ, it.sons[1], stored, conf)
     elif typ.n != nil:
       let field = getField(typ.n, i)
       s.add(escapeJson(field.name.s))
       s.add(": ")
-      storeAny(s, field.typ, it, stored)
+      storeAny(s, field.typ, it, stored, conf)
 
 proc skipColon*(n: PNode): PNode =
   result = n
   if n.kind == nkExprColonExpr:
     result = n.sons[1]
 
-proc storeAny(s: var string; t: PType; a: PNode; stored: var IntSet) =
+proc storeAny(s: var string; t: PType; a: PNode; stored: var IntSet;
+              conf: ConfigRef) =
   case t.kind
   of tyNone: assert false
   of tyBool: s.add($(a.intVal != 0))
@@ -74,7 +76,7 @@ proc storeAny(s: var string; t: PType; a: PNode; stored: var IntSet) =
       s.add("[")
       for i in 0 .. a.len-1:
         if i > 0: s.add(", ")
-        storeAny(s, t.elemType, a[i], stored)
+        storeAny(s, t.elemType, a[i], stored, conf)
       s.add("]")
   of tyTuple:
     s.add("{")
@@ -82,11 +84,11 @@ proc storeAny(s: var string; t: PType; a: PNode; stored: var IntSet) =
       if i > 0: s.add(", ")
       s.add("\"Field" & $i)
       s.add("\": ")
-      storeAny(s, t.sons[i], a[i].skipColon, stored)
+      storeAny(s, t.sons[i], a[i].skipColon, stored, conf)
     s.add("}")
   of tyObject:
     s.add("{")
-    storeObj(s, t, a, stored)
+    storeObj(s, t, a, stored, conf)
     s.add("}")
   of tySet:
     s.add("[")
@@ -94,15 +96,16 @@ proc storeAny(s: var string; t: PType; a: PNode; stored: var IntSet) =
       if i > 0: s.add(", ")
       if a[i].kind == nkRange:
         var x = copyNode(a[i][0])
-        storeAny(s, t.lastSon, x, stored)
+        storeAny(s, t.lastSon, x, stored, conf)
         while x.intVal+1 <= a[i][1].intVal:
           s.add(", ")
-          storeAny(s, t.lastSon, x, stored)
+          storeAny(s, t.lastSon, x, stored, conf)
           inc x.intVal
       else:
-        storeAny(s, t.lastSon, a[i], stored)
+        storeAny(s, t.lastSon, a[i], stored, conf)
     s.add("]")
-  of tyRange, tyGenericInst, tyAlias, tySink: storeAny(s, t.lastSon, a, stored)
+  of tyRange, tyGenericInst, tyAlias, tySink:
+    storeAny(s, t.lastSon, a, stored, conf)
   of tyEnum:
     # we need a slow linear search because of enums with holes:
     for e in items(t.n):
@@ -121,7 +124,7 @@ proc storeAny(s: var string; t: PType; a: PNode; stored: var IntSet) =
       s.add("[")
       s.add($x.ptrToInt)
       s.add(", ")
-      storeAny(s, t.lastSon, a, stored)
+      storeAny(s, t.lastSon, a, stored, conf)
       s.add("]")
   of tyString, tyCString:
     if a.kind == nkNilLit or a.strVal.isNil: s.add("null")
@@ -129,14 +132,15 @@ proc storeAny(s: var string; t: PType; a: PNode; stored: var IntSet) =
   of tyInt..tyInt64, tyUInt..tyUInt64: s.add($a.intVal)
   of tyFloat..tyFloat128: s.add($a.floatVal)
   else:
-    internalError a.info, "cannot marshal at compile-time " & t.typeToString
+    internalError conf, a.info, "cannot marshal at compile-time " & t.typeToString
 
-proc storeAny*(s: var string; t: PType; a: PNode) =
+proc storeAny*(s: var string; t: PType; a: PNode; conf: ConfigRef) =
   var stored = initIntSet()
-  storeAny(s, t, a, stored)
+  storeAny(s, t, a, stored, conf)
 
 proc loadAny(p: var JsonParser, t: PType,
-             tab: var Table[BiggestInt, PNode]): PNode =
+             tab: var Table[BiggestInt, PNode];
+             conf: ConfigRef): PNode =
   case t.kind
   of tyNone: assert false
   of tyBool:
@@ -170,7 +174,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)
+      result.add loadAny(p, t.elemType, tab, conf)
     if p.kind == jsonArrayEnd: next(p)
     else: raiseParseErr(p, "']' end of array expected")
   of tySequence:
@@ -182,7 +186,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)
+        result.add loadAny(p, t.elemType, tab, conf)
       if p.kind == jsonArrayEnd: next(p)
       else: raiseParseErr(p, "")
     else:
@@ -198,7 +202,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)
+      result.add loadAny(p, t.sons[i], tab, conf)
       inc i
     if p.kind == jsonObjectEnd: next(p)
     else: raiseParseErr(p, "'}' end of object expected")
@@ -220,7 +224,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))
+      fieldNode.addSon(loadAny(p, field.typ, tab, conf))
       result.sons[pos] = fieldNode
     if p.kind == jsonObjectEnd: next(p)
     else: raiseParseErr(p, "'}' end of object expected")
@@ -229,7 +233,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)
+      result.add loadAny(p, t.lastSon, tab, conf)
       next(p)
     if p.kind == jsonArrayEnd: next(p)
     else: raiseParseErr(p, "']' end of array expected")
@@ -248,7 +252,7 @@ proc loadAny(p: var JsonParser, t: PType,
       if p.kind == jsonInt:
         let idx = p.getInt
         next(p)
-        result = loadAny(p, t.lastSon, tab)
+        result = loadAny(p, t.lastSon, tab, conf)
         tab[idx] = result
       else: raiseParseErr(p, "index for ref type expected")
       if p.kind == jsonArrayEnd: next(p)
@@ -275,14 +279,15 @@ proc loadAny(p: var JsonParser, t: PType,
       next(p)
       return
     raiseParseErr(p, "float expected")
-  of tyRange, tyGenericInst, tyAlias, tySink: result = loadAny(p, t.lastSon, tab)
+  of tyRange, tyGenericInst, tyAlias, tySink:
+    result = loadAny(p, t.lastSon, tab, conf)
   else:
-    internalError "cannot marshal at compile-time " & t.typeToString
+    internalError conf, "cannot marshal at compile-time " & t.typeToString
 
-proc loadAny*(s: string; t: PType): PNode =
+proc loadAny*(s: string; t: PType; conf: ConfigRef): PNode =
   var tab = initTable[BiggestInt, PNode]()
   var p: JsonParser
   open(p, newStringStream(s), "unknown file")
   next(p)
-  result = loadAny(p, t, tab)
+  result = loadAny(p, t, tab, conf)
   close(p)
diff --git a/compiler/vmops.nim b/compiler/vmops.nim
index 7f8bf06c1..617295b0d 100644
--- a/compiler/vmops.nim
+++ b/compiler/vmops.nim
@@ -15,8 +15,6 @@ from math import sqrt, ln, log10, log2, exp, round, arccos, arcsin,
 
 from os import getEnv, existsEnv, dirExists, fileExists, putEnv, walkDir
 
-from options import gProjectPath
-
 template mathop(op) {.dirty.} =
   registerCallback(c, "stdlib.math." & astToStr(op), `op Wrapper`)
 
@@ -77,15 +75,15 @@ proc staticWalkDirImpl(path: string, relative: bool): PNode =
     result.add newTree(nkTupleConstr, newIntNode(nkIntLit, k.ord),
                               newStrNode(nkStrLit, f))
 
-proc gorgeExWrapper(a: VmArgs) {.nimcall.} =
-  let (s, e) = opGorge(getString(a, 0), getString(a, 1), getString(a, 2),
-                       a.currentLineInfo)
-  setResult a, newTree(nkTupleConstr, newStrNode(nkStrLit, s), newIntNode(nkIntLit, e))
+proc registerAdditionalOps*(c: PCtx) =
+  proc gorgeExWrapper(a: VmArgs) =
+    let (s, e) = opGorge(getString(a, 0), getString(a, 1), getString(a, 2),
+                         a.currentLineInfo, c.config)
+    setResult a, newTree(nkTupleConstr, newStrNode(nkStrLit, s), newIntNode(nkIntLit, e))
 
-proc getProjectPathWrapper(a: VmArgs) {.nimcall.} =
-  setResult a, gProjectPath
+  proc getProjectPathWrapper(a: VmArgs) =
+    setResult a, c.config.projectPath
 
-proc registerAdditionalOps*(c: PCtx) =
   wrap1f_math(sqrt)
   wrap1f_math(ln)
   wrap1f_math(log10)
diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim
index 76d91d4e7..91b527e02 100644
--- a/compiler/wordrecg.nim
+++ b/compiler/wordrecg.nim
@@ -55,7 +55,8 @@ type
     wFloatchecks, wNanChecks, wInfChecks, wMoveChecks,
     wAssertions, wPatterns, wWarnings,
     wHints, wOptimization, wRaises, wWrites, wReads, wSize, wEffects, wTags,
-    wDeadCodeElim, wSafecode, wPackage, wNoForward, wReorder, wNoRewrite,
+    wDeadCodeElimUnused,  # deprecated, dead code elim always happens
+    wSafecode, wPackage, wNoForward, wReorder, wNoRewrite,
     wPragma,
     wCompileTime, wNoInit,
     wPassc, wPassl, wBorrow, wDiscardable,
@@ -143,7 +144,8 @@ const
 
     "assertions", "patterns", "warnings", "hints",
     "optimization", "raises", "writes", "reads", "size", "effects", "tags",
-    "deadcodeelim", "safecode", "package", "noforward", "reorder", "norewrite",
+    "deadcodeelim",  # deprecated, dead code elim always happens
+    "safecode", "package", "noforward", "reorder", "norewrite",
     "pragma",
     "compiletime", "noinit",
     "passc", "passl", "borrow", "discardable", "fieldchecks",
diff --git a/compiler/writetracking.nim b/compiler/writetracking.nim
index e03d6fb59..c0f7b7b20 100644
--- a/compiler/writetracking.nim
+++ b/compiler/writetracking.nim
@@ -15,7 +15,7 @@
 ##   * 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
+import intsets, idents, ast, astalgo, trees, renderer, msgs, types, options
 
 const
   debug = false
@@ -180,7 +180,7 @@ proc deps(w: var W; n: PNode) =
       let last = lastSon(child)
       if last.kind == nkEmpty: continue
       if child.kind == nkVarTuple and last.kind in {nkPar, nkTupleConstr}:
-        internalAssert child.len-2 == last.len
+        if child.len-2 != last.len: return
         for i in 0 .. child.len-3:
           deps(w, child.sons[i], last.sons[i], {})
       else:
@@ -220,7 +220,7 @@ proc possibleAliases(w: var W; result: var seq[ptr TSym]) =
         # x = f(..., y, ....)
         for i in 0 ..< a.srcNoTc: addNoDup a.src[i]
 
-proc markWriteOrEscape(w: var W) =
+proc markWriteOrEscape(w: var W; conf: ConfigRef) =
   ## Both 'writes' and 'escapes' effects ultimately only care
   ## about *parameters*.
   ## However, due to aliasing, even locals that might not look as parameters
@@ -249,7 +249,7 @@ proc markWriteOrEscape(w: var W) =
         if p.kind == skParam and p.owner == w.owner:
           incl(p.flags, sfWrittenTo)
           if w.owner.kind == skFunc and p.typ.kind != tyVar:
-            localError(a.info, "write access to non-var parameter: " & p.name.s)
+            localError(conf, a.info, "write access to non-var parameter: " & p.name.s)
 
     if {rootIsResultOrParam, rootIsHeapAccess, markAsEscaping}*a.destInfo != {}:
       var destIsParam = false
@@ -263,14 +263,14 @@ proc markWriteOrEscape(w: var W) =
           if p.kind == skParam and p.owner == w.owner:
             incl(p.flags, sfEscapes)
 
-proc trackWrites*(owner: PSym; body: PNode) =
+proc trackWrites*(owner: PSym; body: PNode; conf: ConfigRef) =
   var w: W
   w.owner = owner
   w.assignments = @[]
   # Phase 1: Collect and preprocess any assignments in the proc body:
   deps(w, body)
   # Phase 2: Compute the 'writes' and 'escapes' effects:
-  markWriteOrEscape(w)
+  markWriteOrEscape(w, conf)
   if w.returnsNew != asgnOther and not isEmptyType(owner.typ.sons[0]) and
       containsGarbageCollectedRef(owner.typ.sons[0]):
     incl(owner.typ.flags, tfReturnsNew)
diff --git a/config/nim.cfg b/config/nim.cfg
index a2a774b23..e11826587 100644
--- a/config/nim.cfg
+++ b/config/nim.cfg
@@ -118,7 +118,7 @@ path="$lib/pure"
 # Configuration for the GNU C/C++ compiler:
 @if windows:
   #gcc.path = r"$nim\dist\mingw\bin"
-  @if gcc:
+  @if gcc or tcc:
     tlsEmulation:on
   @end
 @end
@@ -243,3 +243,11 @@ vcc.cpp.options.size = "/O1"
 
 # Configuration for the Tiny C Compiler:
 tcc.options.always = "-w"
+
+# Configuration for the Genode toolchain
+amd64.genode.gcc.cpp.exe = "genode-x86-g++"
+amd64.genode.gcc.exe = "genode-x86-gcc"
+amd64.genode.gcc.path = "/usr/local/genode-gcc/bin"
+arm.genode.gcc.cpp.exe = "genode-arm-g++"
+arm.genode.gcc.exe = "genode-arm-gcc"
+arm.genode.gcc.path = "/usr/local/genode-gcc/bin"
diff --git a/doc/advopt.txt b/doc/advopt.txt
index 214ac8dd2..685c8127d 100644
--- a/doc/advopt.txt
+++ b/doc/advopt.txt
@@ -35,7 +35,8 @@ Advanced options:
   --noLinking               compile Nim and generated files but do not link
   --noMain                  do not generate a main procedure
   --genScript               generate a compile script (in the 'nimcache'
-                            subdirectory named 'compile_$project$scriptext')
+                            subdirectory named 'compile_$$project$$scriptext'),
+                            implies --compileOnly
   --genDeps                 generate a '.deps' file containing the dependencies
   --os:SYMBOL               set the target operating system (cross-compilation)
   --cpu:SYMBOL              set the target processor (cross-compilation)
@@ -65,6 +66,8 @@ Advanced options:
   --excessiveStackTrace:on|off
                             stack traces use full file paths
   --oldNewlines:on|off      turn on|off the old behaviour of "\n"
+  --laxStrings:on|off       when turned on, accessing the zero terminator in
+                            strings is allowed; only for backwards compatibility
   --skipCfg                 do not read the general configuration file
   --skipUserCfg             do not read the user's configuration file
   --skipParentCfg           do not read the parent dirs' configuration files
@@ -76,7 +79,7 @@ Advanced options:
   --NimblePath:PATH         add a path for Nimble support
   --noNimblePath            deactivate the Nimble path
   --noCppExceptions         use default exception handling with C++ backend
-  --cppCompileToNamespace   use namespace "Nim" for the generated C++ code 
+  --cppCompileToNamespace   use namespace "Nim" for the generated C++ code
   --excludePath:PATH        exclude a path from the list of search paths
   --dynlibOverride:SYMBOL   marks SYMBOL so that dynlib:SYMBOL
                             has no effect and can be statically linked instead;
@@ -88,5 +91,6 @@ Advanced options:
   --parallelBuild:0|1|...   perform a parallel build
                             value = number of processors (0 for auto-detect)
   --verbosity:0|1|2|3       set Nim's verbosity level (1 is default)
-  --experimental            enable experimental language features
+  --experimental:$1
+                            enable experimental language feature
   -v, --version             show detailed version information
diff --git a/doc/basicopt.txt b/doc/basicopt.txt
index 4db2d5af7..90c7ba09c 100644
--- a/doc/basicopt.txt
+++ b/doc/basicopt.txt
@@ -29,14 +29,13 @@ Options:
   --nanChecks:on|off        turn NaN checks on|off
   --infChecks:on|off        turn Inf checks on|off
   --nilChecks:on|off        turn nil checks on|off
-  --deadCodeElim:on|off     whole program dead code elimination on|off
   --opt:none|speed|size     optimize not at all or for speed|size
                             Note: use -d:release for a release build!
   --debugger:native|endb    use native debugger (gdb) | ENDB (experimental)
   --app:console|gui|lib|staticlib
                             generate a console app|GUI app|DLL|static library
   -r, --run                 run the compiled program with given arguments
-  --advanced                show advanced command line switches
+  --fullhelp                show all command line switches
   -h, --help                show this help
 
 Note, single letter options that take an argument require a colon. E.g. -p:PATH.
diff --git a/doc/koch.rst b/doc/koch.rst
index ff62b8186..35cf9d8b6 100644
--- a/doc/koch.rst
+++ b/doc/koch.rst
@@ -35,32 +35,8 @@ options:
   By default a debug version is created, passing this option will
   force a release build, which is much faster and should be preferred
   unless you are debugging the compiler.
--d:useGnuReadline
-  Includes the `rdstdin module <rdstdin.html>`_ for `interactive
-  mode <nimc.html#nim-interactive-mode>`_ (aka ``nim i``).
-  This is not needed on Windows. On other platforms this may
-  incorporate the GNU readline library.
--d:nativeStacktrace
-  Use native stack traces (only for Mac OS X or Linux).
--d:avoidTimeMachine
-  Only for Mac OS X, activating this switch will force excluding
-  the generated ``nimcache`` directories from Time Machine backups.
-  By default ``nimcache`` directories will be included in backups,
-  and just for the Nim compiler itself it means backing up 20MB
-  of generated files each time you update the compiler. Using this
-  option will make the compiler invoke the `tmutil
-  <https://developer.apple.com/library/mac/documentation/Darwin/Reference/Manpages/man8/tmutil.8.html>`_
-  command on all ``nimcache`` directories, setting their backup
-  exclusion bit.
-
-  You can use the following command to locate all ``nimcache``
-  directories and check their backup exclusion bit::
-
-    $ find . -type d -name nimcache -exec tmutil isexcluded \{\} \;
---gc:refc|v2|markAndSweep|boehm|go|none
-  Selects which garbage collection strategy to use for the compiler
-  and generated code. See the `Nim's Garbage Collector <gc.html>`_
-  documentation for more information.
+-d:useLinenoise
+  Use the linenoise library for interactive mode (not needed on Windows).
 
 After compilation is finished you will hopefully end up with the nim
 compiler in the ``bin`` directory. You can add Nim's ``bin`` directory to
@@ -104,8 +80,3 @@ be rerun in serial fashion so that meaninful error output can be gathered for
 inspection. The ``--parallelBuild:n`` switch or configuration option can be
 used to force a specific number of parallel jobs or run everything serially
 from the start (``n == 1``).
-
-zip command
------------
-
-The `zip`:idx: command builds the installation ZIP package.
diff --git a/doc/manual.rst b/doc/manual.rst
index b114f0a8a..44e2ee5b5 100644
--- a/doc/manual.rst
+++ b/doc/manual.rst
@@ -1397,10 +1397,10 @@ dereferencing operations for reference types:
 
 Automatic dereferencing is also performed for the first argument of a routine
 call. But currently this feature has to be only enabled
-via ``{.experimental.}``:
+via ``{.experimental: "implicitDeref".}``:
 
 .. code-block:: nim
-  {.experimental.}
+  {.experimental: "implicitDeref".}
 
   proc depth(x: NodeObj): int = ...
 
@@ -1471,7 +1471,7 @@ mysterious crashes.
 
 **Note**: The example only works because the memory is initialized to zero
 (``alloc0`` instead of ``alloc`` does this): ``d.s`` is thus initialized to
-``nil`` which the string assignment can handle. One needs to know low level
+binary zero which the string assignment can handle. One needs to know low level
 details like this when mixing garbage collected data with unmanaged memory.
 
 .. XXX finalizers for traced objects
@@ -2512,8 +2512,8 @@ char                            '\\0'
 bool                            false
 ref or pointer type             nil
 procedural type                 nil
-sequence                        nil (*not* ``@[]``)
-string                          nil (*not* "")
+sequence                        ``@[]``
+string                          ``""``
 tuple[x: A, y: B, ...]          (default(A), default(B), ...)
                                 (analogous for objects)
 array[0..., T]                  [default(T), ...]
@@ -3487,17 +3487,17 @@ returned value is an l-value and can be modified by the caller:
 .. code-block:: nim
   var g = 0
 
-  proc WriteAccessToG(): var int =
+  proc writeAccessToG(): var int =
     result = g
 
-  WriteAccessToG() = 6
+  writeAccessToG() = 6
   assert g == 6
 
 It is a compile time error if the implicitly introduced pointer could be
 used to access a location beyond its lifetime:
 
 .. code-block:: nim
-  proc WriteAccessToG(): var int =
+  proc writeAccessToG(): var int =
     var g = 0
     result = g # Error!
 
@@ -3512,6 +3512,24 @@ In the standard library every name of a routine that returns a ``var`` type
 starts with the prefix ``m`` per convention.
 
 
+.. include:: manual/var_t_return.rst
+
+Future directions
+~~~~~~~~~~~~~~~~~
+
+Later versions of Nim can be more precise about the borrowing rule with
+a syntax like:
+
+.. code-block:: nim
+  proc foo(other: Y; container: var X): var T from container
+
+Here ``var T from container`` explicitly exposes that the
+location is deviated from the second parameter (called
+'container' in this case). The syntax ``var T from p`` specifies a type
+``varTy[T, 2]`` which is incompatible with ``varTy[T, 1]``.
+
+
+
 Overloading of the subscript operator
 -------------------------------------
 
@@ -4252,7 +4270,7 @@ therefore very useful for type specialization within generic code:
     Table[Key, Value] = object
       keys: seq[Key]
       values: seq[Value]
-      when not (Key is string): # nil value for strings used for optimization
+      when not (Key is string): # empty value for strings used for optimization
         deletedKeys: seq[bool]
 
 
@@ -5241,15 +5259,21 @@ chance to convert it into a sequence.
 Macros
 ======
 
-A macro is a special kind of low level template. Macros can be used
-to implement `domain specific languages`:idx:.
+A macro is a special function that is executed at compile-time.
+Normally the input for a macro is an abstract syntax
+tree (AST) of the code that is passed to it. The macro can then do
+transformations on it and return the transformed AST. The
+transformed AST is then passed to the compiler as if the macro
+invocation would have been replaced by its result in the source
+code. This can be used to implement `domain specific
+languages`:idx:.
 
 While macros enable advanced compile-time code transformations, they
 cannot change Nim's syntax. However, this is no real restriction because
 Nim's syntax is flexible enough anyway.
 
 To write macros, one needs to know how the Nim concrete syntax is converted
-to an abstract syntax tree.
+to an AST.
 
 There are two ways to invoke a macro:
 (1) invoking a macro like a procedure call (`expression macros`)
@@ -5269,19 +5293,21 @@ variable number of arguments:
   # ``macros`` module:
   import macros
 
-  macro debug(n: varargs[untyped]): untyped =
-    # `n` is a Nim AST that contains the whole macro invocation
-    # this macro returns a list of statements:
-    result = newNimNode(nnkStmtList, n)
+  macro debug(args: varargs[untyped]): untyped =
+    # `args` is a collection of `NimNode` values that each contain the
+    # AST for an argument of the macro. A macro always has to
+    # return a `NimNode`. A node of kind `nnkStmtList` is suitable for
+    # this use case.
+    result = nnkStmtList.newTree()
     # iterate over any argument that is passed to this macro:
-    for i in 0..n.len-1:
+    for n in args:
       # add a call to the statement list that writes the expression;
       # `toStrLit` converts an AST to its string representation:
-      add(result, newCall("write", newIdentNode("stdout"), toStrLit(n[i])))
+      result.add newCall("write", newIdentNode("stdout"), newLit(n.repr))
       # add a call to the statement list that writes ": "
-      add(result, newCall("write", newIdentNode("stdout"), newStrLitNode(": ")))
+      result.add newCall("write", newIdentNode("stdout"), newLit(": "))
       # add a call to the statement list that writes the expressions value:
-      add(result, newCall("writeLine", newIdentNode("stdout"), n[i]))
+      result.add newCall("writeLine", newIdentNode("stdout"), n)
 
   var
     a: array[0..10, int]
@@ -5562,7 +5588,7 @@ dot operators
 -------------
 
 **Note**: Dot operators are still experimental and so need to be enabled
-via ``{.experimental.}``.
+via ``{.experimental: "dotOperators".}``.
 
 Nim offers a special family of dot operators that can be used to
 intercept and rewrite proc call and field access attempts, referring
@@ -6272,6 +6298,9 @@ modules don't need to import a module's dependencies:
   var x: MyObject
   echo $x
 
+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 /
@@ -6742,22 +6771,6 @@ the created global variables within a module is not defined, but all of them
 will be initialized after any top-level variables in their originating module
 and before any variable in a module that imports it.
 
-deadCodeElim pragma
--------------------
-The ``deadCodeElim`` pragma only applies to whole modules: It tells the
-compiler to activate (or deactivate) dead code elimination for the module the
-pragma appears in.
-
-The ``--deadCodeElim:on`` command line switch has the same effect as marking
-every module with ``{.deadCodeElim:on}``. However, for some modules such as
-the GTK wrapper it makes sense to *always* turn on dead code elimination -
-no matter if it is globally active or not.
-
-Example:
-
-.. code-block:: nim
-  {.deadCodeElim: on.}
-
 
 ..
   NoForward pragma
@@ -6875,17 +6888,12 @@ is uncertain (it may be removed any time).
 Example:
 
 .. code-block:: nim
-  {.experimental.}
-  type
-    FooId = distinct int
-    BarId = distinct int
-  using
-    foo: FooId
-    bar: BarId
+  {.experimental: "parallel".}
 
   proc useUsing(bar, foo) =
-    echo "bar is of type BarId"
-    echo "foo is of type FooId"
+    parallel:
+      for i in 0..4:
+        echo "echo in parallel"
 
 
 Implementation Specific Pragmas
@@ -7429,8 +7437,8 @@ code generation directly, but their presence can be detected by macros.
 Custom pragmas are defined using templates annotated with pragma ``pragma``:
 
 .. code-block:: nim
-  template dbTable(name: string, table_space: string = nil) {.pragma.}
-  template dbKey(name: string = nil, primary_key: bool = false) {.pragma.}
+  template dbTable(name: string, table_space: string = "") {.pragma.}
+  template dbKey(name: string = "", primary_key: bool = false) {.pragma.}
   template dbForeignKey(t: typedesc) {.pragma.}
   template dbIgnore {.pragma.}
 
@@ -7793,8 +7801,9 @@ Future directions:
 Threadvar pragma
 ----------------
 
-A global variable can be marked with the ``threadvar`` pragma; it is
-a `thread-local`:idx: variable then:
+A variable can be marked with the ``threadvar`` pragma, which makes it a
+`thread-local`:idx: variable; Additionally, this implies all the effects
+of the ``global`` pragma.
 
 .. code-block:: nim
   var checkpoints* {.threadvar.}: seq[string]
@@ -7907,7 +7916,7 @@ Example:
 
   # Compute PI in an inefficient way
   import strutils, math, threadpool
-  {.experimental.}
+  {.experimental: "parallel".}
 
   proc term(k: float): float = 4 * math.pow(-1, k) / (2*k + 1)
 
@@ -8187,5 +8196,3 @@ validation errors:
 
 If the taint mode is turned off, ``TaintedString`` is simply an alias for
 ``string``.
-
-
diff --git a/doc/manual/var_t_return.rst b/doc/manual/var_t_return.rst
new file mode 100644
index 000000000..b9ff1d892
--- /dev/null
+++ b/doc/manual/var_t_return.rst
@@ -0,0 +1,20 @@
+Memory safety for returning by ``var T`` is ensured by a simple borrowing
+rule: If ``result`` does not refer to a location pointing to the heap
+(that is in ``result = X`` the ``X`` involves a ``ptr`` or ``ref`` access)
+then it has to be deviated by the routine's first parameter:
+
+.. code-block:: nim
+  proc forward[T](x: var T): var T =
+    result = x # ok, deviated from the first parameter.
+
+  proc p(param: var int): var int =
+    var x: int
+    # we know 'forward' provides a view into the location deviated by
+    # its first argument 'x'.
+    result = forward(x) # Error: location is derived from ``x``
+                        # which is not p's first parameter and lives
+                        # on the stack.
+
+In other words, the lifetime of what ``result`` points to is attached to the
+lifetime of the first parameter and that is enough knowledge to verify
+memory safety at the callsite.
diff --git a/doc/nimc.rst b/doc/nimc.rst
index b275438ea..29dbdea42 100644
--- a/doc/nimc.rst
+++ b/doc/nimc.rst
@@ -181,7 +181,7 @@ generated; use the ``--symbolFiles:on`` command line switch to activate them.
 
 Unfortunately due to technical reasons the ``--symbolFiles:on`` needs
 to *aggregate* some generated C code. This means that the resulting executable
-might contain some cruft even when dead code elimination is turned on. So
+might contain some cruft even with dead code elimination. So
 the final release build should be done with ``--symbolFiles:off``.
 
 Due to the aggregation of C code it is also recommended that each project
@@ -439,7 +439,7 @@ target.
 
 For example, to generate code for an `AVR`:idx: processor use this command::
 
-  nim c --cpu:avr --os:standalone --deadCodeElim:on --genScript x.nim
+  nim c --cpu:avr --os:standalone --genScript x.nim
 
 For the ``standalone`` target one needs to provide
 a file ``panicoverride.nim``.
diff --git a/doc/tut1.rst b/doc/tut1.rst
index cbfef183e..f2251c463 100644
--- a/doc/tut1.rst
+++ b/doc/tut1.rst
@@ -944,12 +944,8 @@ String variables are **mutable**, so appending to a string
 is possible, and quite efficient. Strings in Nim are both zero-terminated and have a
 length field. A string's length can be retrieved with the builtin ``len``
 procedure; the length never counts the terminating zero. Accessing the
-terminating zero is not an error and often leads to simpler code:
-
-.. code-block:: nim
-  if s[i] == 'a' and s[i+1] == 'b':
-    # no need to check whether ``i < len(s)``!
-    ...
+terminating zero is an error, it only exists so that a Nim string can be converted
+to a ``cstring`` without doing a copy.
 
 The assignment operator for strings copies the string. You can use the ``&``
 operator to concatenate strings and ``add`` to append to a string.
@@ -960,11 +956,7 @@ enforced. For example, when reading strings from binary files, they are merely
 a sequence of bytes. The index operation ``s[i]`` means the i-th *char* of
 ``s``, not the i-th *unichar*.
 
-String variables are initialized with a special value, called ``nil``. However,
-most string operations cannot deal with ``nil`` (leading to an exception being
-raised) for performance reasons. It is best to use empty strings ``""``
-rather than ``nil`` as the *empty* value. But ``""`` often creates a string
-object on the heap, so there is a trade-off to be made here.
+A string variable is initialized with the empty string ``""``.
 
 
 Integers
@@ -1309,11 +1301,7 @@ Example:
     x: seq[int] # a reference to a sequence of integers
   x = @[1, 2, 3, 4, 5, 6] # the @ turns the array into a sequence allocated on the heap
 
-Sequence variables are initialized with ``nil``. However, most sequence
-operations cannot deal with ``nil`` (leading to an exception being
-raised) for performance reasons. Thus one should use empty sequences ``@[]``
-rather than ``nil`` as the *empty* value. But ``@[]`` creates a sequence
-object on the heap, so there is a trade-off to be made here.
+Sequence variables are initialized with ``@[]``.
 
 The ``for`` statement can be used with one or two variables when used with a
 sequence. When you use the one variable form, the variable will hold the value
@@ -1355,11 +1343,9 @@ type does not matter.
 .. code-block:: nim
     :test: "nim c $1"
   var
-    fruits:   seq[string]       # reference to a sequence of strings that is initialized with 'nil'
+    fruits:   seq[string]       # reference to a sequence of strings that is initialized with '@[]'
     capitals: array[3, string]  # array of strings with a fixed size
 
-  fruits = @[]                  # creates an empty sequence on the heap that will be referenced by 'fruits'
-
   capitals = ["New York", "London", "Berlin"]   # array 'capitals' allows assignment of only three elements
   fruits.add("Banana")          # sequence 'fruits' is dynamically expandable during runtime
   fruits.add("Mango")
@@ -1691,7 +1677,7 @@ rules apply:
   write(stdout, x(3))   # no error: A.x is called
   write(stdout, x(""))  # no error: B.x is called
 
-  proc x*(a: int): string = nil
+  proc x*(a: int): string = discard
   write(stdout, x(3))   # ambiguous: which `x` is to call?
 
 
diff --git a/examples/httpserver2.nim b/examples/httpserver2.nim
index 050684d3e..1843ff967 100644
--- a/examples/httpserver2.nim
+++ b/examples/httpserver2.nim
@@ -107,7 +107,7 @@ proc executeCgi(server: var TServer, client: Socket, path, query: string,
       dataAvail = recvLine(client, buf)
       if buf.len == 0:
         break
-      var L = toLower(buf)
+      var L = toLowerAscii(buf)
       if L.startsWith("content-length:"):
         var i = len("content-length:")
         while L[i] in Whitespace: inc(i)
@@ -205,7 +205,7 @@ proc acceptRequest(server: var TServer, client: Socket) =
     client.close()
   else:
     when defined(Windows):
-      var ext = splitFile(path).ext.toLower
+      var ext = splitFile(path).ext.toLowerAscii
       if ext == ".exe" or ext == ".cgi":
         # XXX: extract interpreter information here?
         cgi = true
diff --git a/koch.nim b/koch.nim
index d51b902ee..4f85c6583 100644
--- a/koch.nim
+++ b/koch.nim
@@ -47,7 +47,6 @@ Boot options:
   -d:release               produce a release version of the compiler
   -d:useLinenoise          use the linenoise library for interactive mode
                            (not needed on Windows)
-  -d:avoidTimeMachine      only for Mac OS X, excludes nimcache dir from backups
 
 Commands for core developers:
   web [options]            generates the website and the full documentation
@@ -252,7 +251,7 @@ proc xz(args: string) =
 
 proc buildTool(toolname, args: string) =
   nimexec("cc $# $#" % [args, toolname])
-  copyFile(dest="bin"/ splitFile(toolname).name.exe, source=toolname.exe)
+  copyFile(dest="bin" / splitFile(toolname).name.exe, source=toolname.exe)
 
 proc buildTools(latest: bool) =
   let nimsugExe = "bin/nimsuggest".exe
diff --git a/lib/core/macros.nim b/lib/core/macros.nim
index 7217475c6..fa5cd67df 100644
--- a/lib/core/macros.nim
+++ b/lib/core/macros.nim
@@ -14,11 +14,8 @@ include "system/inclrtl"
 
 ## .. include:: ../../doc/astspec.txt
 
-# If you look for the implementation of the magic symbols, copy the
-# magic string and open the file "../../compiler/vm.nim" and search
-# for the magic string with the prefix "opc". For example the
-# implementation of ``{.magic: "FooBar".}`` will be right under
-# ``of opcFooBar:``.
+# If you look for the implementation of the magic symbol
+# ``{.magic: "Foo".}``, search for `mFoo` and `opcFoo`.
 
 type
   NimNodeKind* = enum
@@ -388,7 +385,7 @@ type
 
 {.deprecated: [TBindSymRule: BindSymRule].}
 
-proc bindSym*(ident: string, rule: BindSymRule = brClosed): NimNode {.
+proc bindSym*(ident: static[string], rule: BindSymRule = brClosed): NimNode {.
               magic: "NBindSym", noSideEffect.}
   ## creates a node that binds `ident` to a symbol node. The bound symbol
   ## may be an overloaded symbol.
@@ -709,8 +706,7 @@ proc treeRepr*(n: NimNode): string {.compileTime, benign.} =
     res.add(($n.kind).substr(3))
 
     case n.kind
-    of nnkEmpty: discard # same as nil node in this representation
-    of nnkNilLit: res.add(" nil")
+    of nnkEmpty, nnkNilLit: discard # same as nil node in this representation
     of nnkCharLit..nnkInt64Lit: res.add(" " & $n.intVal)
     of nnkFloatLit..nnkFloat64Lit: res.add(" " & $n.floatVal)
     of nnkStrLit..nnkTripleStrLit, nnkIdent, nnkSym:
@@ -733,8 +729,7 @@ proc lispRepr*(n: NimNode): string {.compileTime, benign.} =
   add(result, "(")
 
   case n.kind
-  of nnkEmpty: discard # same as nil node in this representation
-  of nnkNilLit: add(result, "nil")
+  of nnkEmpty, nnkNilLit: discard # same as nil node in this representation
   of nnkCharLit..nnkInt64Lit: add(result, $n.intVal)
   of nnkFloatLit..nnkFloat64Lit: add(result, $n.floatVal)
   of nnkStrLit..nnkTripleStrLit, nnkCommentStmt, nnkident, nnkSym:
@@ -769,7 +764,7 @@ proc astGenRepr*(n: NimNode): string {.compileTime, benign.} =
   ## See also `repr`, `treeRepr`, and `lispRepr`.
 
   const
-    NodeKinds = {nnkEmpty, nnkNilLit, nnkIdent, nnkSym, nnkNone, nnkCommentStmt}
+    NodeKinds = {nnkEmpty, nnkIdent, nnkSym, nnkNone, nnkCommentStmt}
     LitKinds = {nnkCharLit..nnkInt64Lit, nnkFloatLit..nnkFloat64Lit, nnkStrLit..nnkTripleStrLit}
 
   proc traverse(res: var string, level: int, n: NimNode) {.benign.} =
@@ -778,12 +773,13 @@ proc astGenRepr*(n: NimNode): string {.compileTime, benign.} =
       res.add("new" & ($n.kind).substr(3) & "Node(")
     elif n.kind in LitKinds:
       res.add("newLit(")
+    elif n.kind == nnkNilLit:
+      res.add("newNilLit()")
     else:
       res.add($n.kind)
 
     case n.kind
-    of nnkEmpty: discard
-    of nnkNilLit: res.add("nil")
+    of nnkEmpty, nnkNilLit: discard
     of nnkCharLit: res.add("'" & $chr(n.intVal) & "'")
     of nnkIntLit..nnkInt64Lit: res.add($n.intVal)
     of nnkFloatLit..nnkFloat64Lit: res.add($n.floatVal)
@@ -1292,7 +1288,11 @@ proc customPragmaNode(n: NimNode): NimNode =
     typ = n.getTypeInst()
 
   if typ.typeKind == ntyTypeDesc:
-    return typ[1].getImpl()[0][1]
+    let impl = typ[1].getImpl()
+    if impl[0].kind == nnkPragmaExpr:
+      return impl[0][1]
+    else:
+      return impl[0] # handle types which don't have macro at all
 
   if n.kind == nnkSym: # either an variable or a proc
     let impl = n.getImpl()
diff --git a/lib/deprecated/pure/asyncio.nim b/lib/deprecated/pure/asyncio.nim
index 5fd45b215..34cabefb0 100644
--- a/lib/deprecated/pure/asyncio.nim
+++ b/lib/deprecated/pure/asyncio.nim
@@ -101,8 +101,8 @@ when defined(windows):
   from winlean import TimeVal, SocketHandle, FD_SET, FD_ZERO, TFdSet,
     FD_ISSET, select
 else:
-  from posix import TimeVal, SocketHandle, FD_SET, FD_ZERO, TFdSet,
-    FD_ISSET, select
+  from posix import TimeVal, Time, Suseconds, SocketHandle, FD_SET, FD_ZERO,
+    TFdSet, FD_ISSET, select
 
 type
   DelegateObj* = object
@@ -556,8 +556,12 @@ proc send*(sock: AsyncSocket, data: string) =
 proc timeValFromMilliseconds(timeout = 500): Timeval =
   if timeout != -1:
     var seconds = timeout div 1000
-    result.tv_sec = seconds.int32
-    result.tv_usec = ((timeout - seconds * 1000) * 1000).int32
+    when defined(posix):
+      result.tv_sec = seconds.Time
+      result.tv_usec = ((timeout - seconds * 1000) * 1000).Suseconds
+    else:
+      result.tv_sec = seconds.int32
+      result.tv_usec = ((timeout - seconds * 1000) * 1000).int32
 
 proc createFdSet(fd: var TFdSet, s: seq[Delegate], m: var int) =
   FD_ZERO(fd)
diff --git a/lib/deprecated/pure/ftpclient.nim b/lib/deprecated/pure/ftpclient.nim
index ed2f14450..7645258b6 100644
--- a/lib/deprecated/pure/ftpclient.nim
+++ b/lib/deprecated/pure/ftpclient.nim
@@ -12,7 +12,7 @@ import sockets, strutils, parseutils, times, os, asyncio
 
 from asyncnet import nil
 from nativesockets import nil
-from asyncdispatch import PFuture
+from asyncdispatch import Future
 ## **Note**: This module is deprecated since version 0.11.3.
 ## You should use the async version of this module
 ## `asyncftpclient <asyncftpclient.html>`_.
diff --git a/lib/deprecated/pure/sockets.nim b/lib/deprecated/pure/sockets.nim
index f068c7d56..05aebef76 100644
--- a/lib/deprecated/pure/sockets.nim
+++ b/lib/deprecated/pure/sockets.nim
@@ -32,7 +32,7 @@
 
 include "system/inclrtl"
 
-{.deadCodeElim: on.}
+{.deadCodeElim: on.}  # dce option deprecated
 
 when hostOS == "solaris":
   {.passl: "-lsocket -lnsl".}
@@ -953,8 +953,12 @@ when defined(ssl):
 proc timeValFromMilliseconds(timeout = 500): Timeval =
   if timeout != -1:
     var seconds = timeout div 1000
-    result.tv_sec = seconds.int32
-    result.tv_usec = ((timeout - seconds * 1000) * 1000).int32
+    when defined(posix):
+      result.tv_sec = seconds.Time
+      result.tv_usec = ((timeout - seconds * 1000) * 1000).Suseconds
+    else:
+      result.tv_sec = seconds.int32
+      result.tv_usec = ((timeout - seconds * 1000) * 1000).int32
 
 proc createFdSet(fd: var TFdSet, s: seq[Socket], m: var int) =
   FD_ZERO(fd)
diff --git a/lib/impure/db_sqlite.nim b/lib/impure/db_sqlite.nim
index 21049571f..b1541c1ba 100644
--- a/lib/impure/db_sqlite.nim
+++ b/lib/impure/db_sqlite.nim
@@ -81,7 +81,7 @@
 ##
 ##  theDb.close()
 
-{.deadCodeElim:on.}
+{.deadCodeElim: on.}  # dce option deprecated
 
 import strutils, sqlite3
 
@@ -126,6 +126,7 @@ proc tryExec*(db: DbConn, query: SqlQuery,
               args: varargs[string, `$`]): bool {.
               tags: [ReadDbEffect, WriteDbEffect].} =
   ## tries to execute the query and returns true if successful, false otherwise.
+  assert(not db.isNil, "Database not connected.")
   var q = dbFormat(query, args)
   var stmt: sqlite3.Pstmt
   if prepare_v2(db, q, q.len.cint, stmt, nil) == SQLITE_OK:
@@ -144,6 +145,7 @@ proc newRow(L: int): Row =
 
 proc setupQuery(db: DbConn, query: SqlQuery,
                 args: varargs[string]): Pstmt =
+  assert(not db.isNil, "Database not connected.")
   var q = dbFormat(query, args)
   if prepare_v2(db, q, q.len.cint, result, nil) != SQLITE_OK: dbError(db)
 
@@ -267,6 +269,7 @@ proc tryInsertID*(db: DbConn, query: SqlQuery,
                   {.tags: [WriteDbEffect], raises: [].} =
   ## executes the query (typically "INSERT") and returns the
   ## generated ID for the row or -1 in case of an error.
+  assert(not db.isNil, "Database not connected.")
   var q = dbFormat(query, args)
   var stmt: sqlite3.Pstmt
   result = -1
diff --git a/lib/impure/nre.nim b/lib/impure/nre.nim
index 3d4afc0ae..6058128dd 100644
--- a/lib/impure/nre.nim
+++ b/lib/impure/nre.nim
@@ -10,7 +10,7 @@
 from pcre import nil
 import nre.private.util
 import tables
-from strutils import toLower, `%`
+from strutils import `%`
 from math import ceil
 import options
 from unicode import runeLenAt
@@ -326,15 +326,15 @@ proc `$`*(pattern: RegexMatch): string =
 
 proc `==`*(a, b: Regex): bool =
   if not a.isNil and not b.isNil:
-    return a.pattern   == b.pattern and
-           a.pcreObj   == b.pcreObj and
+    return a.pattern == b.pattern and
+           a.pcreObj == b.pcreObj and
            a.pcreExtra == b.pcreExtra
   else:
     return system.`==`(a, b)
 
 proc `==`*(a, b: RegexMatch): bool =
   return a.pattern == b.pattern and
-         a.str     == b.str
+         a.str == b.str
 # }}}
 
 # Creation & Destruction {{{
@@ -645,7 +645,6 @@ template replaceImpl(str: string, pattern: Regex,
     let bounds = match.matchBounds
     result.add(str.substr(lastIdx, bounds.a - 1))
     let nextVal = replacement
-    assert(nextVal != nil)
     result.add(nextVal)
 
     lastIdx = bounds.b + 1
diff --git a/lib/impure/rdstdin.nim b/lib/impure/rdstdin.nim
index 5aa4cfcc3..54bab82f0 100644
--- a/lib/impure/rdstdin.nim
+++ b/lib/impure/rdstdin.nim
@@ -13,7 +13,7 @@
 ## is used. This suffices because Windows' console already provides the
 ## wanted functionality.
 
-{.deadCodeElim: on.}
+{.deadCodeElim: on.}  # dce option deprecated
 
 when defined(Windows):
   proc readLineFromStdin*(prompt: string): TaintedString {.
diff --git a/lib/js/dom.nim b/lib/js/dom.nim
index cd7609729..fd81fdf3f 100644
--- a/lib/js/dom.nim
+++ b/lib/js/dom.nim
@@ -62,6 +62,7 @@ type
     frames*: seq[TFrame]
     screen*: Screen
     performance*: Performance
+    onpopstate*: proc (event: Event)
 
   Frame* = ref FrameObj
   FrameObj {.importc.} = object of WindowObj
@@ -175,6 +176,12 @@ type
     text*: cstring
     value*: cstring
 
+  TextAreaElement* = ref object of ElementObj
+    value*: cstring
+    selectionStart*, selectionEnd*: int
+    selectionDirection*: cstring
+    rows*, cols*: int
+
   FormElement* = ref FormObj
   FormObj {.importc.} = object of ElementObj
     action*: cstring
@@ -446,6 +453,7 @@ type
 proc addEventListener*(et: EventTarget, ev: cstring, cb: proc(ev: Event), useCapture: bool = false)
 proc addEventListener*(et: EventTarget, ev: cstring, cb: proc(ev: Event), options: AddEventListenerOptions)
 proc removeEventListener*(et: EventTarget, ev: cstring, cb: proc(ev: Event), useCapture: bool = false)
+proc dispatchEvent*(et: EventTarget, ev: Event)
 
 # Window "methods"
 proc alert*(w: Window, msg: cstring)
@@ -500,7 +508,7 @@ proc removeAttributeNode*(n, attr: Node)
 proc removeChild*(n, child: Node)
 proc replaceChild*(n, newNode, oldNode: Node)
 proc replaceData*(n: Node, start, len: int, text: cstring)
-proc scrollIntoView*(n: Node)
+proc scrollIntoView*(n: Node, alignToTop: bool=true)
 proc setAttribute*(n: Node, name, value: cstring)
 proc setAttributeNode*(n: Node, attr: Node)
 
@@ -596,6 +604,7 @@ proc parseFloat*(s: cstring): BiggestFloat {.importc, nodecl.}
 proc parseInt*(s: cstring): int {.importc, nodecl.}
 proc parseInt*(s: cstring, radix: int):int {.importc, nodecl.}
 
+proc newEvent*(name: cstring): Event {.importcpp: "new Event(@)", constructor.}
 
 type
   TEventHandlers* {.deprecated.} = EventTargetObj
diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim
index 223fc836a..adac16777 100644
--- a/lib/packages/docutils/rst.nim
+++ b/lib/packages/docutils/rst.nim
@@ -43,8 +43,8 @@ type
     mwUnsupportedField
 
   MsgHandler* = proc (filename: string, line, col: int, msgKind: MsgKind,
-                       arg: string) {.nimcall.} ## what to do in case of an error
-  FindFileHandler* = proc (filename: string): string {.nimcall.}
+                       arg: string) {.closure.} ## what to do in case of an error
+  FindFileHandler* = proc (filename: string): string {.closure.}
 
 const
   messages: array[MsgKind, string] = [
@@ -853,7 +853,6 @@ type
   DirKind = enum             # must be ordered alphabetically!
     dkNone, dkAuthor, dkAuthors, dkCode, dkCodeBlock, dkContainer, dkContents,
     dkFigure, dkImage, dkInclude, dkIndex, dkRaw, dkTitle
-{.deprecated: [TDirKind: DirKind].}
 
 const
   DirIds: array[0..12, string] = ["", "author", "authors", "code",
@@ -1114,7 +1113,6 @@ proc parseHeadline(p: var RstParser): PRstNode =
 
 type
   IntSeq = seq[int]
-{.deprecated: [TIntSeq: IntSeq].}
 
 proc tokEnd(p: RstParser): int =
   result = p.tok[p.idx].col + len(p.tok[p.idx].symbol) - 1
@@ -1408,8 +1406,6 @@ type
     hasArg, hasOptions, argIsFile, argIsWord
   DirFlags = set[DirFlag]
   SectionParser = proc (p: var RstParser): PRstNode {.nimcall.}
-{.deprecated: [TDirFlag: DirFlag, TDirFlags: DirFlags,
-              TSectionParser: SectionParser].}
 
 proc parseDirective(p: var RstParser, flags: DirFlags): PRstNode =
   ## Parses arguments and options for a directive block.
diff --git a/lib/packages/docutils/rstast.nim b/lib/packages/docutils/rstast.nim
index 7be4470c1..f3596b571 100644
--- a/lib/packages/docutils/rstast.nim
+++ b/lib/packages/docutils/rstast.nim
@@ -70,8 +70,6 @@ type
                               ## the document or the section
     level*: int               ## valid for some node kinds
     sons*: RstNodeSeq        ## the node's sons
-{.deprecated: [TRstNodeKind: RstNodeKind, TRstNodeSeq: RstNodeSeq,
-              TRstNode: RstNode].}
 
 proc len*(n: PRstNode): int =
   result = len(n.sons)
@@ -99,7 +97,6 @@ type
   RenderContext {.pure.} = object
     indent: int
     verbatim: int
-{.deprecated: [TRenderContext: RenderContext].}
 
 proc renderRstToRst(d: var RenderContext, n: PRstNode,
                     result: var string) {.gcsafe.}
diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim
index ee1f0076c..1547c888d 100644
--- a/lib/packages/docutils/rstgen.nim
+++ b/lib/packages/docutils/rstgen.nim
@@ -774,7 +774,7 @@ proc renderImage(d: PDoc, n: PRstNode, result: var string) =
                                    Digits + Letters + WhiteSpace)
   let
     arg = getArgument(n)
-    isObject = arg.toLower().endsWith(".svg")
+    isObject = arg.toLowerAscii().endsWith(".svg")
   var
     options = ""
     content = ""
diff --git a/lib/posix/epoll.nim b/lib/posix/epoll.nim
index c5ed1a873..2d38137bb 100644
--- a/lib/posix/epoll.nim
+++ b/lib/posix/epoll.nim
@@ -7,7 +7,7 @@
 #    distribution, for details about the copyright.
 #
 
-{.deadCodeElim:on.}
+{.deadCodeElim: on.}  # dce option deprecated
 
 from posix import SocketHandle
 
diff --git a/lib/posix/inotify.nim b/lib/posix/inotify.nim
index a206f8067..359e88617 100644
--- a/lib/posix/inotify.nim
+++ b/lib/posix/inotify.nim
@@ -7,7 +7,7 @@
 #    distribution, for details about the copyright.
 #
 
-{.deadCodeElim:on.}
+{.deadCodeElim: on.}  # dce option deprecated
 
 # Get the platform-dependent flags.
 # Structure describing an inotify event.
diff --git a/lib/posix/kqueue.nim b/lib/posix/kqueue.nim
index 730491a53..18b47f5d5 100644
--- a/lib/posix/kqueue.nim
+++ b/lib/posix/kqueue.nim
@@ -7,8 +7,6 @@
 #    distribution, for details about the copyright.
 #
 
-{.deadCodeElim:on.}
-
 from posix import Timespec
 
 when defined(macosx) or defined(freebsd) or defined(openbsd) or
@@ -61,7 +59,7 @@ const
   EV_CLEAR*    = 0x0020 ## Clear event state after reporting.
   EV_RECEIPT*  = 0x0040 ## Force EV_ERROR on success, data == 0
   EV_DISPATCH* = 0x0080 ## Disable event after reporting.
-  
+
   EV_SYSFLAGS* = 0xF000 ## Reserved by system
   EV_DROP*     = 0x1000 ## Not should be dropped
   EV_FLAG1*    = 0x2000 ## Filter-specific flag
@@ -87,10 +85,10 @@ when defined(macosx) or defined(freebsd) or defined(dragonfly):
     NOTE_FFAND*      = 0x40000000'u32 ## AND fflags
     NOTE_FFOR*       = 0x80000000'u32 ## OR fflags
     NOTE_FFCOPY*     = 0xc0000000'u32 ## copy fflags
-    NOTE_FFCTRLMASK* = 0xc0000000'u32 ## masks for operations 
+    NOTE_FFCTRLMASK* = 0xc0000000'u32 ## masks for operations
     NOTE_FFLAGSMASK* = 0x00ffffff'u32
 
-    NOTE_TRIGGER*    = 0x01000000'u32 ## Cause the event to be triggered 
+    NOTE_TRIGGER*    = 0x01000000'u32 ## Cause the event to be triggered
                                       ## for output.
 
 # data/hint flags for EVFILT_{READ|WRITE}, shared with userspace
diff --git a/lib/posix/linux.nim b/lib/posix/linux.nim
index 6f9f75e34..25c7c7979 100644
--- a/lib/posix/linux.nim
+++ b/lib/posix/linux.nim
@@ -1,4 +1,4 @@
-{.deadCodeElim:on.}
+{.deadCodeElim: on.}  # dce option deprecated
 
 import posix
 
diff --git a/lib/posix/posix.nim b/lib/posix/posix.nim
index a23d76050..3ff156bdf 100644
--- a/lib/posix/posix.nim
+++ b/lib/posix/posix.nim
@@ -27,10 +27,10 @@
 ## resulting C code will just ``#include <XYZ.h>`` and *not* define the
 ## symbols declared here.
 
-# This ensures that we don't accidentally generate #includes for files that
-# might not exist on a specific platform! The user will get an error only
-# if they actualy try to use the missing declaration
-{.deadCodeElim: on.}
+# Dead code elimination ensures that we don't accidentally generate #includes
+# for files that might not exist on a specific platform! The user will get an
+# error only if they actualy try to use the missing declaration
+{.deadCodeElim: on.}  # dce option deprecated
 
 # TODO these constants don't seem to be fetched from a header file for unknown
 #      platforms - where do they come from and why are they here?
diff --git a/lib/posix/posix_linux_amd64.nim b/lib/posix/posix_linux_amd64.nim
index 9e6211b63..4f114d394 100644
--- a/lib/posix/posix_linux_amd64.nim
+++ b/lib/posix/posix_linux_amd64.nim
@@ -351,8 +351,8 @@ type
 
   Timeval* {.importc: "struct timeval", header: "<sys/select.h>",
              final, pure.} = object ## struct timeval
-    tv_sec*: clong       ## Seconds.
-    tv_usec*: clong ## Microseconds.
+    tv_sec*: Time       ## Seconds.
+    tv_usec*: Suseconds ## Microseconds.
   TFdSet* {.importc: "fd_set", header: "<sys/select.h>",
            final, pure.} = object
     abi: array[1024 div (8 * sizeof(clong)), clong]
diff --git a/lib/posix/posix_other.nim b/lib/posix/posix_other.nim
index 01bc1c1e5..004a4205b 100644
--- a/lib/posix/posix_other.nim
+++ b/lib/posix/posix_other.nim
@@ -7,7 +7,7 @@
 #    distribution, for details about the copyright.
 #
 
-{.deadCodeElim:on.}
+{.deadCodeElim: on.}  # dce option deprecated
 
 const
   hasSpawnH = not defined(haiku) # should exist for every Posix system nowadays
@@ -335,8 +335,8 @@ type
 
   Timeval* {.importc: "struct timeval", header: "<sys/select.h>",
              final, pure.} = object ## struct timeval
-    tv_sec*: int       ## Seconds.
-    tv_usec*: int ## Microseconds.
+    tv_sec*: Time ## Seconds.
+    tv_usec*: Suseconds ## Microseconds.
   TFdSet* {.importc: "fd_set", header: "<sys/select.h>",
            final, pure.} = object
   Mcontext* {.importc: "mcontext_t", header: "<ucontext.h>",
diff --git a/lib/posix/termios.nim b/lib/posix/termios.nim
index f86e408fb..60d540107 100644
--- a/lib/posix/termios.nim
+++ b/lib/posix/termios.nim
@@ -7,7 +7,7 @@
 #    distribution, for details about the copyright.
 #
 
-{.deadCodeElim: on.}
+{.deadCodeElim: on.}  # dce option deprecated
 import posix
 
 type
diff --git a/lib/pure/algorithm.nim b/lib/pure/algorithm.nim
index 45e031574..2b668d1ca 100644
--- a/lib/pure/algorithm.nim
+++ b/lib/pure/algorithm.nim
@@ -13,9 +13,6 @@ type
   SortOrder* = enum   ## sort order
     Descending, Ascending
 
-{.deprecated: [TSortOrder: SortOrder].}
-
-
 proc `*`*(x: int, order: SortOrder): int {.inline.} =
   ## flips `x` if ``order == Descending``;
   ## if ``order == Ascending`` then `x` is returned.
@@ -69,21 +66,25 @@ proc reversed*[T](a: openArray[T]): seq[T] =
 
 proc binarySearch*[T](a: openArray[T], key: T): int =
   ## binary search for `key` in `a`. Returns -1 if not found.
-  var b = len(a)
-  while result < b:
-    var mid = (result + b) div 2
-    if a[mid] < key: result = mid + 1
-    else: b = mid
-  if result >= len(a) or a[result] != key: result = -1
-
-proc smartBinarySearch*[T](a: openArray[T], key: T): int =
-  ## ``a.len`` must be a power of 2 for this to work.
-  var step = a.len div 2
-  while step > 0:
-    if a[result or step] <= key:
-      result = result or step
-    step = step shr 1
-  if a[result] != key: result = -1
+  if ((a.len - 1) and a.len) == 0 and a.len > 0:
+    # when `a.len` is a power of 2, a faster div can be used.
+    var step = a.len div 2
+    while step > 0:
+      if a[result or step] <= key:
+        result = result or step
+      step = step shr 1
+    if a[result] != key: result = -1
+  else:
+    var b = len(a)
+    while result < b:
+      var mid = (result + b) div 2
+      if a[mid] < key: result = mid + 1
+      else: b = mid
+    if result >= len(a) or a[result] != key: result = -1
+
+proc smartBinarySearch*[T](a: openArray[T], key: T): int {.deprecated.} =
+  ## **Deprecated since version 0.18.1**; Use ``binarySearch`` instead.
+  binarySearch(a,key)
 
 const
   onlySafeCode = true
@@ -363,10 +364,11 @@ when isMainModule:
   var srt1 = [1,2,3,4,4,4,4,5]
   var srt2 = ["iello","hello"]
   var srt3 = [1.0,1.0,1.0]
-  var srt4: seq[int] = @[]
+  var srt4: seq[int]
   assert srt1.isSorted(cmp) == true
   assert srt2.isSorted(cmp) == false
   assert srt3.isSorted(cmp) == true
+  assert srt4.isSorted(cmp) == true
   var srtseq = newSeq[int]()
   assert srtseq.isSorted(cmp) == true
   # Tests for reversed
@@ -395,7 +397,7 @@ proc rotateInternal[T](arg: var openarray[T]; first, middle, last: int): int =
 
   swap(arg[mFirst], arg[next])
   mFirst += 1
-  next  += 1
+  next += 1
   if mFirst == mMiddle:
     mMiddle = next
 
@@ -514,4 +516,24 @@ when isMainModule:
 
   block fillEmptySeq:
     var s = newSeq[int]()
-    s.fill(0)
\ No newline at end of file
+    s.fill(0)
+
+  block testBinarySearch:
+    var noData: seq[int]
+    doAssert binarySearch(noData, 7) == -1
+    let oneData = @[1]
+    doAssert binarySearch(oneData, 1) == 0
+    doAssert binarySearch(onedata, 7) == -1
+    let someData = @[1,3,4,7]
+    doAssert binarySearch(someData, 1) == 0
+    doAssert binarySearch(somedata, 7) == 3
+    doAssert binarySearch(someData, -1) == -1
+    doAssert binarySearch(someData, 5) == -1
+    doAssert binarySearch(someData, 13) == -1
+    let moreData = @[1,3,5,7,4711]
+    doAssert binarySearch(moreData, -1) == -1
+    doAssert binarySearch(moreData,  1) == 0
+    doAssert binarySearch(moreData,  5) == 2
+    doAssert binarySearch(moreData,  6) == -1
+    doAssert binarySearch(moreData,  4711) == 4
+    doAssert binarySearch(moreData,  4712) == -1
diff --git a/lib/pure/asyncfutures.nim b/lib/pure/asyncfutures.nim
index 6df6527d5..863a6843b 100644
--- a/lib/pure/asyncfutures.nim
+++ b/lib/pure/asyncfutures.nim
@@ -27,8 +27,6 @@ type
   FutureError* = object of Exception
     cause*: FutureBase
 
-{.deprecated: [PFutureBase: FutureBase, PFuture: Future].}
-
 when not defined(release):
   var currentID = 0
 
@@ -177,7 +175,7 @@ proc fail*[T](future: Future[T], error: ref Exception) =
     if getStackTrace(error) == "": getStackTrace() else: getStackTrace(error)
   future.callbacks.call()
 
-proc clearCallbacks(future: FutureBase) =
+proc clearCallbacks*(future: FutureBase) =
   future.callbacks.function = nil
   future.callbacks.next = nil
 
diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim
index ba1615651..fe5a835d7 100644
--- a/lib/pure/asynchttpserver.nim
+++ b/lib/pure/asynchttpserver.nim
@@ -60,9 +60,6 @@ type
     reusePort: bool
     maxBody: int ## The maximum content-length that will be read for the body.
 
-{.deprecated: [TRequest: Request, PAsyncHttpServer: AsyncHttpServer,
-  THttpCode: HttpCode, THttpVersion: HttpVersion].}
-
 proc newAsyncHttpServer*(reuseAddr = true, reusePort = false,
                          maxBody = 8388608): AsyncHttpServer =
   ## Creates a new ``AsyncHttpServer`` instance.
diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim
index a75f9daac..e7552e3e3 100644
--- a/lib/pure/asyncnet.nim
+++ b/lib/pure/asyncnet.nim
@@ -134,8 +134,6 @@ type
     protocol: Protocol
   AsyncSocket* = ref AsyncSocketDesc
 
-{.deprecated: [PAsyncSocket: AsyncSocket].}
-
 proc newAsyncSocket*(fd: AsyncFD, domain: Domain = AF_INET,
     sockType: SockType = SOCK_STREAM,
     protocol: Protocol = IPPROTO_TCP, buffered = true): AsyncSocket =
@@ -201,7 +199,7 @@ when defineSsl:
       flags: set[SocketFlag]) {.async.} =
     let len = bioCtrlPending(socket.bioOut)
     if len > 0:
-      var data = newStringOfCap(len)
+      var data = newString(len)
       let read = bioRead(socket.bioOut, addr data[0], len)
       assert read != 0
       if read < 0:
diff --git a/lib/pure/base64.nim b/lib/pure/base64.nim
index 4b0d08292..bfb8a1666 100644
--- a/lib/pure/base64.nim
+++ b/lib/pure/base64.nim
@@ -52,7 +52,7 @@ template encodeInternal(s: typed, lineLen: int, newLine: string): untyped =
   if numLines > 0: inc(total, (numLines - 1) * newLine.len)
 
   result = newString(total)
-  var 
+  var
     i = 0
     r = 0
     currLine = 0
@@ -76,7 +76,7 @@ template encodeInternal(s: typed, lineLen: int, newLine: string): untyped =
       currLine = 0
 
   if i < s.len-1:
-    let 
+    let
       a = ord(s[i])
       b = ord(s[i+1])
     result[r] = cb64[a shr 2]
@@ -130,11 +130,11 @@ proc decode*(s: string): string =
   # total is an upper bound, as we will skip arbitrary whitespace:
   result = newString(total)
 
-  var 
+  var
     i = 0
     r = 0
   while true:
-    while s[i] in Whitespace: inc(i)
+    while i < s.len and s[i] in Whitespace: inc(i)
     if i < s.len-3:
       let
         a = s[i].decodeByte
diff --git a/lib/pure/cgi.nim b/lib/pure/cgi.nim
index 5de6aa487..e0cdc2ec0 100644
--- a/lib/pure/cgi.nim
+++ b/lib/pure/cgi.nim
@@ -64,8 +64,6 @@ type
     methodPost,          ## query uses the POST method
     methodGet            ## query uses the GET method
 
-{.deprecated: [TRequestMethod: RequestMethod, ECgi: CgiError].}
-
 proc cgiError*(msg: string) {.noreturn.} =
   ## raises an ECgi exception with message `msg`.
   var e: ref CgiError
@@ -97,11 +95,10 @@ iterator decodeData*(data: string): tuple[key, value: TaintedString] =
   var name = ""
   var value = ""
   # decode everything in one pass:
-  while data[i] != '\0':
+  while i < data.len:
     setLen(name, 0) # reuse memory
-    while true:
+    while i < data.len:
       case data[i]
-      of '\0': break
       of '%':
         var x = 0
         handleHexChar(data[i+1], x)
@@ -112,15 +109,16 @@ iterator decodeData*(data: string): tuple[key, value: TaintedString] =
       of '=', '&': break
       else: add(name, data[i])
       inc(i)
-    if data[i] != '=': cgiError("'=' expected")
+    if i >= data.len or data[i] != '=': cgiError("'=' expected")
     inc(i) # skip '='
     setLen(value, 0) # reuse memory
-    while true:
+    while i < data.len:
       case data[i]
       of '%':
         var x = 0
-        handleHexChar(data[i+1], x)
-        handleHexChar(data[i+2], x)
+        if i+2 < data.len:
+          handleHexChar(data[i+1], x)
+          handleHexChar(data[i+2], x)
         inc(i, 2)
         add(value, chr(x))
       of '+': add(value, ' ')
@@ -128,9 +126,9 @@ iterator decodeData*(data: string): tuple[key, value: TaintedString] =
       else: add(value, data[i])
       inc(i)
     yield (name.TaintedString, value.TaintedString)
-    if data[i] == '&': inc(i)
-    elif data[i] == '\0': break
-    else: cgiError("'&' expected")
+    if i < data.len:
+      if data[i] == '&': inc(i)
+      else: cgiError("'&' expected")
 
 iterator decodeData*(allowedMethods: set[RequestMethod] =
        {methodNone, methodPost, methodGet}): tuple[key, value: TaintedString] =
diff --git a/lib/pure/collections/critbits.nim b/lib/pure/collections/critbits.nim
index 34f5c5470..5ae5e26b2 100644
--- a/lib/pure/collections/critbits.nim
+++ b/lib/pure/collections/critbits.nim
@@ -74,18 +74,19 @@ proc rawInsert[T](c: var CritBitTree[T], key: string): Node[T] =
     var newByte = 0
     block blockX:
       while newbyte < key.len:
-        if it.key[newbyte] != key[newbyte]:
-          newotherbits = it.key[newbyte].ord xor key[newbyte].ord
+        let ch = if newbyte < it.key.len: it.key[newbyte] else: '\0'
+        if ch != key[newbyte]:
+          newotherbits = ch.ord xor key[newbyte].ord
           break blockX
         inc newbyte
-      if it.key[newbyte] != '\0':
+      if newbyte < it.key.len:
         newotherbits = it.key[newbyte].ord
       else:
         return it
     while (newOtherBits and (newOtherBits-1)) != 0:
       newOtherBits = newOtherBits and (newOtherBits-1)
     newOtherBits = newOtherBits xor 255
-    let ch = it.key[newByte]
+    let ch = if newByte < it.key.len: it.key[newByte] else: '\0'
     let dir = (1 + (ord(ch) or newOtherBits)) shr 8
 
     var inner: Node[T]
@@ -162,13 +163,13 @@ proc containsOrIncl*(c: var CritBitTree[void], key: string): bool =
   var n = rawInsert(c, key)
   result = c.count == oldCount
 
-proc inc*(c: var CritBitTree[int]; key: string) =
-  ## counts the 'key'.
+proc inc*(c: var CritBitTree[int]; key: string, val: int = 1) =
+  ## increments `c[key]` by `val`.
   let oldCount = c.count
   var n = rawInsert(c, key)
-  if c.count == oldCount:
+  if c.count == oldCount or oldCount == 0:
     # not a new key:
-    inc n.val
+    inc n.val, val
 
 proc incl*(c: var CritBitTree[void], key: string) =
   ## includes `key` in `c`.
@@ -351,3 +352,13 @@ when isMainModule:
   assert toSeq(r.items) == @["abc", "definition", "prefix", "xyz"]
 
   assert toSeq(r.itemsWithPrefix("de")) == @["definition"]
+  var c = CritBitTree[int]()
+
+  c.inc("a")
+  assert c["a"] == 1
+
+  c.inc("a", 4)
+  assert c["a"] == 5
+
+  c.inc("a", -5)
+  assert c["a"] == 0
diff --git a/lib/pure/collections/sets.nim b/lib/pure/collections/sets.nim
index 32b6387ad..59c90bc2b 100644
--- a/lib/pure/collections/sets.nim
+++ b/lib/pure/collections/sets.nim
@@ -120,6 +120,13 @@ iterator items*[A](s: HashSet[A]): A =
   for h in 0..high(s.data):
     if isFilled(s.data[h].hcode): yield s.data[h].key
 
+proc hash*[A](s: HashSet[A]): Hash =
+  ## hashing of HashSet
+  assert s.isValid, "The set needs to be initialized."
+  for h in 0..high(s.data):
+    result = result xor s.data[h].hcode
+  result = !$result
+
 const
   growthFactor = 2
 
@@ -690,6 +697,13 @@ iterator items*[A](s: OrderedSet[A]): A =
   forAllOrderedPairs:
     yield s.data[h].key
 
+proc hash*[A](s: OrderedSet[A]): Hash =
+  ## hashing of OrderedSet
+  assert s.isValid, "The set needs to be initialized."
+  forAllOrderedPairs:
+    result = result !& s.data[h].hcode
+  result = !$result
+
 iterator pairs*[A](s: OrderedSet[A]): tuple[a: int, b: A] =
   assert s.isValid, "The set needs to be initialized"
   forAllOrderedPairs:
diff --git a/lib/pure/colors.nim b/lib/pure/colors.nim
index 4ec76dee0..25a2fb870 100644
--- a/lib/pure/colors.nim
+++ b/lib/pure/colors.nim
@@ -14,8 +14,6 @@ import strutils
 type
   Color* = distinct int ## a color stored as RGB
 
-{.deprecated: [TColor: Color].}
-
 proc `==` *(a, b: Color): bool {.borrow.}
   ## compares two colors.
 
diff --git a/lib/pure/complex.nim b/lib/pure/complex.nim
index ccde5ee0a..98cab1a5a 100644
--- a/lib/pure/complex.nim
+++ b/lib/pure/complex.nim
@@ -25,8 +25,6 @@ type
   Complex* = tuple[re, im: float]
     ## a complex number, consisting of a real and an imaginary part
 
-{.deprecated: [TComplex: Complex].}
-
 proc toComplex*(x: SomeInteger): Complex =
   ## Convert some integer ``x`` to a complex number.
   result.re = x
diff --git a/lib/pure/concurrency/threadpool.nim b/lib/pure/concurrency/threadpool.nim
index a5eaec86e..6ec71e912 100644
--- a/lib/pure/concurrency/threadpool.nim
+++ b/lib/pure/concurrency/threadpool.nim
@@ -168,6 +168,15 @@ proc wakeupWorkerToProcessQueue(w: ptr Worker) =
     signal(w.q.empty)
   signal(w.taskArrived)
 
+proc attach(fv: FlowVarBase; i: int): bool =
+  acquire(fv.cv.L)
+  if fv.cv.counter <= 0:
+    fv.idx = i
+    result = true
+  else:
+    result = false
+  release(fv.cv.L)
+
 proc finished(fv: FlowVarBase) =
   doAssert fv.ai.isNil, "flowVar is still attached to an 'awaitAny'"
   # we have to protect against the rare cases where the owner of the flowVar
@@ -245,26 +254,27 @@ proc `^`*[T](fv: FlowVar[T]): T =
 proc awaitAny*(flowVars: openArray[FlowVarBase]): int =
   ## awaits any of the given flowVars. Returns the index of one flowVar for
   ## which a value arrived. A flowVar only supports one call to 'awaitAny' at
-  ## the same time. That means if you await([a,b]) and await([b,c]) the second
+  ## the same time. That means if you awaitAny([a,b]) and awaitAny([b,c]) the second
   ## call will only await 'c'. If there is no flowVar left to be able to wait
   ## on, -1 is returned.
-  ## **Note**: This results in non-deterministic behaviour and so should be
-  ## avoided.
+  ## **Note**: This results in non-deterministic behaviour and should be avoided.
   var ai: AwaitInfo
   ai.cv.initSemaphore()
   var conflicts = 0
+  result = -1
   for i in 0 .. flowVars.high:
     if cas(addr flowVars[i].ai, nil, addr ai):
-      flowVars[i].idx = i
+      if not attach(flowVars[i], i):
+        result = i
+        break
     else:
       inc conflicts
   if conflicts < flowVars.len:
-    await(ai.cv)
-    result = ai.idx
+    if result < 0:
+      await(ai.cv)
+      result = ai.idx
     for i in 0 .. flowVars.high:
       discard cas(addr flowVars[i].ai, addr ai, nil)
-  else:
-    result = -1
   destroySemaphore(ai.cv)
 
 proc isReady*(fv: FlowVarBase): bool =
@@ -321,7 +331,7 @@ proc slave(w: ptr Worker) {.thread.} =
     await(w.taskArrived)
     # XXX Somebody needs to look into this (why does this assertion fail
     # in Visual Studio?)
-    when not defined(vcc): assert(not w.ready)
+    when not defined(vcc) and not defined(tcc): assert(not w.ready)
 
     withLock numSlavesLock:
       inc numSlavesRunning
diff --git a/lib/pure/cookies.nim b/lib/pure/cookies.nim
index aca0ac2b4..eaff86ae6 100644
--- a/lib/pure/cookies.nim
+++ b/lib/pure/cookies.nim
@@ -25,16 +25,16 @@ proc parseCookies*(s: string): StringTableRef =
   result = newStringTable(modeCaseInsensitive)
   var i = 0
   while true:
-    while s[i] == ' ' or s[i] == '\t': inc(i)
+    while i < s.len and (s[i] == ' ' or s[i] == '\t'): inc(i)
     var keystart = i
-    while s[i] != '=' and s[i] != '\0': inc(i)
+    while i < s.len and s[i] != '=': inc(i)
     var keyend = i-1
-    if s[i] == '\0': break
+    if i >= s.len: break
     inc(i) # skip '='
     var valstart = i
-    while s[i] != ';' and s[i] != '\0': inc(i)
+    while i < s.len and s[i] != ';': inc(i)
     result[substr(s, keystart, keyend)] = substr(s, valstart, i-1)
-    if s[i] == '\0': break
+    if i >= s.len: break
     inc(i) # skip ';'
 
 proc setCookie*(key, value: string, domain = "", path = "",
diff --git a/lib/pure/db_common.nim b/lib/pure/db_common.nim
index 957389605..f8689024b 100644
--- a/lib/pure/db_common.nim
+++ b/lib/pure/db_common.nim
@@ -83,9 +83,6 @@ type
     foreignKey*: bool  ## is this a foreign key?
   DbColumns* = seq[DbColumn]
 
-{.deprecated: [EDb: DbError, TSqlQuery: SqlQuery, FDb: DbEffect,
-              FReadDb: ReadDbEffect, FWriteDb: WriteDbEffect].}
-
 template sql*(query: string): SqlQuery =
   ## constructs a SqlQuery from the string `query`. This is supposed to be
   ## used as a raw-string-literal modifier:
diff --git a/lib/pure/dynlib.nim b/lib/pure/dynlib.nim
index ec6cbb232..5bd06f6fb 100644
--- a/lib/pure/dynlib.nim
+++ b/lib/pure/dynlib.nim
@@ -17,9 +17,9 @@
 ## Loading a simple C function
 ## ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 ##
-## The following example demonstrates loading a function called 'greet' 
+## The following example demonstrates loading a function called 'greet'
 ## from a library that is determined at runtime based upon a language choice.
-## If the library fails to load or the function 'greet' is not found, 
+## If the library fails to load or the function 'greet' is not found,
 ## it quits with a failure error code.
 ##
 ## .. code-block::nim
@@ -59,8 +59,6 @@ import strutils
 type
   LibHandle* = pointer ## a handle to a dynamically loaded library
 
-{.deprecated: [TLibHandle: LibHandle].}
-
 proc loadLib*(path: string, global_symbols=false): LibHandle {.gcsafe.}
   ## loads a library from `path`. Returns nil if the library could not
   ## be loaded.
diff --git a/lib/pure/encodings.nim b/lib/pure/encodings.nim
index 5840d443d..c67cd7579 100644
--- a/lib/pure/encodings.nim
+++ b/lib/pure/encodings.nim
@@ -27,8 +27,6 @@ type
   EncodingError* = object of ValueError ## exception that is raised
                                         ## for encoding errors
 
-{.deprecated: [EInvalidEncoding: EncodingError, PConverter: EncodingConverter].}
-
 when defined(windows):
   proc eqEncodingNames(a, b: string): bool =
     var i = 0
@@ -36,7 +34,7 @@ when defined(windows):
     while i < a.len and j < b.len:
       if a[i] in {'-', '_'}: inc i
       if b[j] in {'-', '_'}: inc j
-      if a[i].toLower != b[j].toLower: return false
+      if i < a.len and j < b.len and a[i].toLowerAscii != b[j].toLowerAscii: return false
       inc i
       inc j
     result = i == a.len and j == b.len
diff --git a/lib/pure/events.nim b/lib/pure/events.nim
index 23a8a2c58..a39dbe3bf 100644
--- a/lib/pure/events.nim
+++ b/lib/pure/events.nim
@@ -43,9 +43,6 @@ type
     s: seq[EventHandler]
   EventError* = object of ValueError
 
-{.deprecated: [TEventArgs: EventArgs, TEventHandler: EventHandler,
-  TEventEmitter: EventEmitter, EInvalidEvent: EventError].}
-
 proc initEventHandler*(name: string): EventHandler =
   ## Initializes an EventHandler with the specified name and returns it.
   result.handlers = @[]
diff --git a/lib/pure/fenv.nim b/lib/pure/fenv.nim
index f8f115ecc..c946c4261 100644
--- a/lib/pure/fenv.nim
+++ b/lib/pure/fenv.nim
@@ -10,7 +10,7 @@
 ## Floating-point environment. Handling of floating-point rounding and
 ## exceptions (overflow, division by zero, etc.).
 
-{.deadCodeElim:on.}
+{.deadCodeElim: on.}  # dce option deprecated
 
 when defined(Posix) and not defined(haiku):
   {.passl: "-lm".}
@@ -102,32 +102,33 @@ proc feupdateenv*(envp: ptr Tfenv): cint {.importc, header: "<fenv.h>".}
   ## represented by object pointed to by `envp` and raise exceptions
   ## according to saved exceptions.
 
-var FP_RADIX_INTERNAL {. importc: "FLT_RADIX" header: "<float.h>" .} : int
-
-template fpRadix* : int = FP_RADIX_INTERNAL
+const 
+  FLT_RADIX = 2 ## the radix of the exponent representation
+
+  FLT_MANT_DIG = 24 ## the number of base FLT_RADIX digits in the mantissa part of a float
+  FLT_DIG = 6 ## the number of digits of precision of a float
+  FLT_MIN_EXP = -125 # the minimum value of base FLT_RADIX in the exponent part of a float
+  FLT_MAX_EXP = 128 # the maximum value of base FLT_RADIX in the exponent part of a float
+  FLT_MIN_10_EXP = -37 ## the minimum value in base 10 of the exponent part of a float
+  FLT_MAX_10_EXP = 38 ## the maximum value in base 10 of the exponent part of a float
+  FLT_MIN = 1.17549435e-38'f32  ## the minimum value of a float
+  FLT_MAX = 3.40282347e+38'f32 ## the maximum value of a float
+  FLT_EPSILON = 1.19209290e-07'f32  ## the difference between 1 and the least value greater than 1 of a float
+ 
+  DBL_MANT_DIG = 53 ## the number of base FLT_RADIX digits in the mantissa part of a double
+  DBL_DIG = 15 ## the number of digits of precision of a double
+  DBL_MIN_EXP = -1021 ## the minimum value of base FLT_RADIX in the exponent part of a double
+  DBL_MAX_EXP = 1024 ## the maximum value of base FLT_RADIX in the exponent part of a double
+  DBL_MIN_10_EXP = -307 ## the minimum value in base 10 of the exponent part of a double
+  DBL_MAX_10_EXP = 308 ## the maximum value in base 10 of the exponent part of a double
+  DBL_MIN = 2.2250738585072014E-308 ## the minimal value of a double
+  DBL_MAX = 1.7976931348623157E+308 ## the minimal value of a double
+  DBL_EPSILON = 2.2204460492503131E-16 ## the difference between 1 and the least value greater than 1 of a double
+
+template fpRadix* : int = FLT_RADIX
   ## The (integer) value of the radix used to represent any floating
   ## point type on the architecture used to build the program.
 
-var FLT_MANT_DIG {. importc: "FLT_MANT_DIG" header: "<float.h>" .} : int
-var FLT_DIG {. importc: "FLT_DIG" header: "<float.h>" .} : int
-var FLT_MIN_EXP {. importc: "FLT_MIN_EXP" header: "<float.h>" .} : int
-var FLT_MAX_EXP {. importc: "FLT_MAX_EXP" header: "<float.h>" .} : int
-var FLT_MIN_10_EXP {. importc: "FLT_MIN_10_EXP" header: "<float.h>" .} : int
-var FLT_MAX_10_EXP {. importc: "FLT_MAX_10_EXP" header: "<float.h>" .} : int
-var FLT_MIN {. importc: "FLT_MIN" header: "<float.h>" .} : cfloat
-var FLT_MAX {. importc: "FLT_MAX" header: "<float.h>" .} : cfloat
-var FLT_EPSILON {. importc: "FLT_EPSILON" header: "<float.h>" .} : cfloat
-
-var DBL_MANT_DIG {. importc: "DBL_MANT_DIG" header: "<float.h>" .} : int
-var DBL_DIG {. importc: "DBL_DIG" header: "<float.h>" .} : int
-var DBL_MIN_EXP {. importc: "DBL_MIN_EXP" header: "<float.h>" .} : int
-var DBL_MAX_EXP {. importc: "DBL_MAX_EXP" header: "<float.h>" .} : int
-var DBL_MIN_10_EXP {. importc: "DBL_MIN_10_EXP" header: "<float.h>" .} : int
-var DBL_MAX_10_EXP {. importc: "DBL_MAX_10_EXP" header: "<float.h>" .} : int
-var DBL_MIN {. importc: "DBL_MIN" header: "<float.h>" .} : cdouble
-var DBL_MAX {. importc: "DBL_MAX" header: "<float.h>" .} : cdouble
-var DBL_EPSILON {. importc: "DBL_EPSILON" header: "<float.h>" .} : cdouble
-
 template mantissaDigits*(T : typedesc[float32]) : int = FLT_MANT_DIG
   ## Number of digits (in base ``floatingPointRadix``) in the mantissa
   ## of 32-bit floating-point numbers.
@@ -179,3 +180,11 @@ template maximumPositiveValue*(T : typedesc[float64]) : float64 = DBL_MAX
 template epsilon*(T : typedesc[float64]): float64 = DBL_EPSILON
   ## The difference between 1.0 and the smallest number greater than
   ## 1.0 that can be represented in a 64-bit floating-point type.
+
+
+when isMainModule:
+  func is_significant(x: float): bool = 
+    if x > minimumPositiveValue(float) and x < maximumPositiveValue(float): true
+    else: false
+
+  doAssert is_significant(10.0)
diff --git a/lib/pure/hashes.nim b/lib/pure/hashes.nim
index b015ed311..6535bb0d3 100644
--- a/lib/pure/hashes.nim
+++ b/lib/pure/hashes.nim
@@ -45,7 +45,6 @@ type
   Hash* = int ## a hash value; hash tables using these values should
                ## always have a size of a power of two and can use the ``and``
                ## operator instead of ``mod`` for truncation of the hash value.
-{.deprecated: [THash: Hash].}
 
 proc `!&`*(h: Hash, val: int): Hash {.inline.} =
   ## mixes a hash value `h` with `val` to produce a new hash value. This is
diff --git a/lib/pure/htmlparser.nim b/lib/pure/htmlparser.nim
index 2f833804d..9d4b57dc0 100644
--- a/lib/pure/htmlparser.nim
+++ b/lib/pure/htmlparser.nim
@@ -178,7 +178,6 @@ type
     tagVar,        ## the HTML ``var`` element
     tagVideo,      ## the HTML ``video`` element
     tagWbr         ## the HTML ``wbr`` element
-{.deprecated: [THtmlTag: HtmlTag].}
 
 const
   tagToStr* = [
diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim
index 70fb19434..3f1a3d067 100644
--- a/lib/pure/httpclient.nim
+++ b/lib/pure/httpclient.nim
@@ -73,12 +73,18 @@
 ## progress of the HTTP request.
 ##
 ## .. code-block:: Nim
-##    var client = newAsyncHttpClient()
+##    import asyncdispatch, httpclient
+##
 ##    proc onProgressChanged(total, progress, speed: BiggestInt) {.async.} =
 ##      echo("Downloaded ", progress, " of ", total)
 ##      echo("Current rate: ", speed div 1000, "kb/s")
-##    client.onProgressChanged = onProgressChanged
-##    discard await client.getContent("http://speedtest-ams2.digitalocean.com/100mb.test")
+##
+##    proc asyncProc() {.async.} =
+##      var client = newAsyncHttpClient()
+##      client.onProgressChanged = onProgressChanged
+##      discard await client.getContent("http://speedtest-ams2.digitalocean.com/100mb.test")
+##
+##    waitFor asyncProc()
 ##
 ## If you would like to remove the callback simply set it to ``nil``.
 ##
@@ -213,10 +219,6 @@ type
                                         ## and ``postContent`` proc,
                                         ## when the server returns an error
 
-{.deprecated: [TResponse: Response, PProxy: Proxy,
-  EInvalidProtocol: ProtocolError, EHttpRequestErr: HttpRequestError
-].}
-
 const defUserAgent* = "Nim httpclient/" & NimVersion
 
 proc httpError(msg: string) =
@@ -241,7 +243,7 @@ proc parseChunks(s: Socket, timeout: int): string =
     var i = 0
     if chunkSizeStr == "":
       httpError("Server terminated connection prematurely")
-    while true:
+    while i < chunkSizeStr.len:
       case chunkSizeStr[i]
       of '0'..'9':
         chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('0'))
@@ -249,8 +251,6 @@ proc parseChunks(s: Socket, timeout: int): string =
         chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('a') + 10)
       of 'A'..'F':
         chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('A') + 10)
-      of '\0':
-        break
       of ';':
         # http://tools.ietf.org/html/rfc2616#section-3.6.1
         # We don't care about chunk-extensions.
@@ -358,8 +358,6 @@ proc parseResponse(s: Socket, getBody: bool, timeout: int): Response =
   else:
     result.body = ""
 
-{.deprecated: [THttpMethod: HttpMethod].}
-
 when not defined(ssl):
   type SSLContext = ref object
 var defaultSSLContext {.threadvar.}: SSLContext
@@ -581,7 +579,7 @@ proc request*(url: string, httpMethod: string, extraHeaders = "",
 
   result = parseResponse(s, httpMethod != "HEAD", timeout)
 
-proc request*(url: string, httpMethod = httpGET, extraHeaders = "",
+proc request*(url: string, httpMethod = HttpGET, extraHeaders = "",
               body = "", sslContext = defaultSSLContext, timeout = -1,
               userAgent = defUserAgent, proxy: Proxy = nil): Response
               {.deprecated.} =
@@ -623,13 +621,13 @@ proc get*(url: string, extraHeaders = "", maxRedirects = 5,
   ## server takes longer than specified an ETimeout exception will be raised.
   ##
   ## **Deprecated since version 0.15.0**: use ``HttpClient.get`` instead.
-  result = request(url, httpGET, extraHeaders, "", sslContext, timeout,
+  result = request(url, HttpGET, extraHeaders, "", sslContext, timeout,
                    userAgent, proxy)
   var lastURL = url
   for i in 1..maxRedirects:
     if result.status.redirection():
       let redirectTo = getNewLocation(lastURL, result.headers)
-      result = request(redirectTo, httpGET, extraHeaders, "", sslContext,
+      result = request(redirectTo, HttpGET, extraHeaders, "", sslContext,
                        timeout, userAgent, proxy)
       lastURL = redirectTo
 
@@ -683,13 +681,13 @@ proc post*(url: string, extraHeaders = "", body = "",
   if not multipart.isNil:
     xh.add(withNewLine("Content-Type: " & mpContentType))
 
-  result = request(url, httpPOST, xh, xb, sslContext, timeout, userAgent,
+  result = request(url, HttpPOST, xh, xb, sslContext, timeout, userAgent,
                    proxy)
   var lastURL = url
   for i in 1..maxRedirects:
     if result.status.redirection():
       let redirectTo = getNewLocation(lastURL, result.headers)
-      var meth = if result.status != "307": httpGet else: httpPost
+      var meth = if result.status != "307": HttpGet else: HttpPost
       result = request(redirectTo, meth, xh, xb, sslContext, timeout,
                        userAgent, proxy)
       lastURL = redirectTo
@@ -739,12 +737,13 @@ proc downloadFile*(url: string, outputFilename: string,
 proc generateHeaders(requestUrl: Uri, httpMethod: string,
                      headers: HttpHeaders, body: string, proxy: Proxy): string =
   # GET
-  result = httpMethod.toUpperAscii()
+  let upperMethod = httpMethod.toUpperAscii()
+  result = upperMethod
   result.add ' '
 
-  if proxy.isNil or (not proxy.isNil and requestUrl.scheme == "https"):
+  if proxy.isNil or requestUrl.scheme == "https":
     # /path?query
-    if requestUrl.path[0] != '/': result.add '/'
+    if not requestUrl.path.startsWith("/"): result.add '/'
     result.add(requestUrl.path)
     if requestUrl.query.len > 0:
       result.add("?" & requestUrl.query)
@@ -768,7 +767,9 @@ proc generateHeaders(requestUrl: Uri, httpMethod: string,
     add(result, "Connection: Keep-Alive\c\L")
 
   # Content length header.
-  if body.len > 0 and not headers.hasKey("Content-Length"):
+  const requiresBody = ["POST", "PUT", "PATCH"]
+  let needsContentLength = body.len > 0 or upperMethod in requiresBody
+  if needsContentLength and not headers.hasKey("Content-Length"):
     add(result, "Content-Length: " & $body.len & "\c\L")
 
   # Proxy auth header.
@@ -929,7 +930,7 @@ proc parseChunks(client: HttpClient | AsyncHttpClient): Future[void]
     var i = 0
     if chunkSizeStr == "":
       httpError("Server terminated connection prematurely")
-    while true:
+    while i < chunkSizeStr.len:
       case chunkSizeStr[i]
       of '0'..'9':
         chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('0'))
@@ -937,8 +938,6 @@ proc parseChunks(client: HttpClient | AsyncHttpClient): Future[void]
         chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('a') + 10)
       of 'A'..'F':
         chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('A') + 10)
-      of '\0':
-        break
       of ';':
         # http://tools.ietf.org/html/rfc2616#section-3.6.1
         # We don't care about chunk-extensions.
diff --git a/lib/pure/httpcore.nim b/lib/pure/httpcore.nim
index f150fa1c1..f85375111 100644
--- a/lib/pure/httpcore.nim
+++ b/lib/pure/httpcore.nim
@@ -45,10 +45,6 @@ type
                       ## TCP/IP tunnel, usually used for proxies.
     HttpPatch         ## Applies partial modifications to a resource.
 
-{.deprecated: [httpGet: HttpGet, httpHead: HttpHead, httpPost: HttpPost,
-               httpPut: HttpPut, httpDelete: HttpDelete, httpTrace: HttpTrace,
-               httpOptions: HttpOptions, httpConnect: HttpConnect].}
-
 
 const
   Http100* = HttpCode(100)
@@ -190,11 +186,11 @@ proc len*(headers: HttpHeaders): int = return headers.table.len
 proc parseList(line: string, list: var seq[string], start: int): int =
   var i = 0
   var current = ""
-  while line[start + i] notin {'\c', '\l', '\0'}:
+  while start+i < line.len and line[start + i] notin {'\c', '\l'}:
     i += line.skipWhitespace(start + i)
     i += line.parseUntil(current, {'\c', '\l', ','}, start + i)
     list.add(current)
-    if line[start + i] == ',':
+    if start+i < line.len and line[start + i] == ',':
       i.inc # Skip ,
     current.setLen(0)
 
diff --git a/lib/pure/httpserver.nim b/lib/pure/httpserver.nim
index 632eb198a..a81e8c0a8 100644
--- a/lib/pure/httpserver.nim
+++ b/lib/pure/httpserver.nim
@@ -110,7 +110,6 @@ when false:
   # TODO: Fix this, or get rid of it.
   type
     RequestMethod = enum reqGet, reqPost
-  {.deprecated: [TRequestMethod: RequestMethod].}
 
   proc executeCgi(client: Socket, path, query: string, meth: RequestMethod) =
     var env = newStringTable(modeCaseInsensitive)
@@ -126,7 +125,7 @@ when false:
       var dataAvail = false
       while dataAvail:
         dataAvail = recvLine(client, buf) # TODO: This is incorrect.
-        var L = toLower(buf.string)
+        var L = toLowerAscii(buf.string)
         if L.startsWith("content-length:"):
           var i = len("content-length:")
           while L[i] in Whitespace: inc(i)
@@ -199,7 +198,7 @@ when false:
       notFound(client)
     else:
       when defined(Windows):
-        var ext = splitFile(path).ext.toLower
+        var ext = splitFile(path).ext.toLowerAscii
         if ext == ".exe" or ext == ".cgi":
           # XXX: extract interpreter information here?
           cgi = true
@@ -225,7 +224,6 @@ type
   PAsyncHTTPServer* = ref AsyncHTTPServer
   AsyncHTTPServer = object of Server
     asyncSocket: AsyncSocket
-{.deprecated: [TAsyncHTTPServer: AsyncHTTPServer, TServer: Server].}
 
 proc open*(s: var Server, port = Port(80), reuseAddr = false) =
   ## creates a new server at port `port`. If ``port == 0`` a free port is
@@ -303,7 +301,7 @@ proc next*(s: var Server) =
   if s.reqMethod == "POST":
     # Check for Expect header
     if s.headers.hasKey("Expect"):
-      if s.headers["Expect"].toLower == "100-continue":
+      if s.headers["Expect"].toLowerAscii == "100-continue":
         s.client.sendStatus("100 Continue")
       else:
         s.client.sendStatus("417 Expectation Failed")
@@ -427,7 +425,7 @@ proc nextAsync(s: PAsyncHTTPServer) =
   if s.reqMethod == "POST":
     # Check for Expect header
     if s.headers.hasKey("Expect"):
-      if s.headers["Expect"].toLower == "100-continue":
+      if s.headers["Expect"].toLowerAscii == "100-continue":
         s.client.sendStatus("100 Continue")
       else:
         s.client.sendStatus("417 Expectation Failed")
diff --git a/lib/pure/includes/asynccommon.nim b/lib/pure/includes/asynccommon.nim
index 45b10c584..06f4958c6 100644
--- a/lib/pure/includes/asynccommon.nim
+++ b/lib/pure/includes/asynccommon.nim
@@ -18,13 +18,13 @@ proc createAsyncNativeSocket*(domain: Domain = Domain.AF_INET,
   createAsyncNativeSocketImpl(domain, sockType, protocol)
 
 proc newAsyncNativeSocket*(domain: cint, sockType: cint,
-                           protocol: cint): AsyncFD {.deprecated.} =
+                           protocol: cint): AsyncFD {.deprecated: "use createAsyncNativeSocket instead".} =
   createAsyncNativeSocketImpl(domain, sockType, protocol)
 
 proc newAsyncNativeSocket*(domain: Domain = Domain.AF_INET,
                            sockType: SockType = SOCK_STREAM,
                            protocol: Protocol = IPPROTO_TCP): AsyncFD
-                           {.deprecated.} =
+                           {.deprecated: "use createAsyncNativeSocket instead".} =
   createAsyncNativeSocketImpl(domain, sockType, protocol)
 
 when defined(windows) or defined(nimdoc):
diff --git a/lib/pure/includes/oserr.nim b/lib/pure/includes/oserr.nim
index 0889d7383..bfb6118f2 100644
--- a/lib/pure/includes/oserr.nim
+++ b/lib/pure/includes/oserr.nim
@@ -12,50 +12,6 @@ when not defined(nimscript):
   when defined(windows):
     import winlean
 
-proc osErrorMsg*(): string {.rtl, extern: "nos$1", deprecated.} =
-  ## Retrieves the operating system's error flag, ``errno``.
-  ## On Windows ``GetLastError`` is checked before ``errno``.
-  ## Returns "" if no error occurred.
-  ##
-  ## **Deprecated since version 0.9.4**: use the other ``osErrorMsg`` proc.
-
-  result = ""
-  when defined(Windows) and not defined(nimscript):
-    var err = getLastError()
-    if err != 0'i32:
-      when useWinUnicode:
-        var msgbuf: WideCString
-        if formatMessageW(0x00000100 or 0x00001000 or 0x00000200 or 0x000000FF,
-                          nil, err, 0, addr(msgbuf), 0, nil) != 0'i32:
-          result = $msgbuf
-          if msgbuf != nil: localFree(cast[pointer](msgbuf))
-      else:
-        var msgbuf: cstring
-        if formatMessageA(0x00000100 or 0x00001000 or 0x00000200 or 0x000000FF,
-                          nil, err, 0, addr(msgbuf), 0, nil) != 0'i32:
-          result = $msgbuf
-          if msgbuf != nil: localFree(msgbuf)
-  when not defined(nimscript):
-    if errno != 0'i32:
-      result = $c_strerror(errno)
-
-{.push warning[deprecated]: off.}
-proc raiseOSError*(msg: string = "") {.noinline, rtl, extern: "nos$1",
-                                       deprecated.} =
-  ## raises an OSError exception with the given message ``msg``.
-  ## If ``msg == ""``, the operating system's error flag
-  ## (``errno``) is converted to a readable error message. On Windows
-  ## ``GetLastError`` is checked before ``errno``.
-  ## If no error flag is set, the message ``unknown OS error`` is used.
-  ##
-  ## **Deprecated since version 0.9.4**: use the other ``raiseOSError`` proc.
-  if len(msg) == 0:
-    var m = osErrorMsg()
-    raise newException(OSError, if m.len > 0: m else: "unknown OS error")
-  else:
-    raise newException(OSError, msg)
-{.pop.}
-
 proc `==`*(err1, err2: OSErrorCode): bool {.borrow.}
 proc `$`*(err: OSErrorCode): string {.borrow.}
 
diff --git a/lib/pure/json.nim b/lib/pure/json.nim
index 6740f483d..ca7e164b8 100644
--- a/lib/pure/json.nim
+++ b/lib/pure/json.nim
@@ -89,510 +89,22 @@
 ##    echo j2
 
 import
-  hashes, tables, strutils, lexbase, streams, unicode, macros
+  hashes, tables, strutils, lexbase, streams, unicode, macros, parsejson
 
 export
   tables.`$`
 
+export
+  parsejson.JsonEventKind, parsejson.JsonError, JsonParser, JsonKindError,
+  open, close, str, getInt, getFloat, kind, getColumn, getLine, getFilename,
+  errorMsg, errorMsgExpected, next, JsonParsingError, raiseParseErr
+
 when defined(nimJsonGet):
   {.pragma: deprecatedGet, deprecated.}
 else:
   {.pragma: deprecatedGet.}
 
 type
-  JsonEventKind* = enum  ## enumeration of all events that may occur when parsing
-    jsonError,           ## an error occurred during parsing
-    jsonEof,             ## end of file reached
-    jsonString,          ## a string literal
-    jsonInt,             ## an integer literal
-    jsonFloat,           ## a float literal
-    jsonTrue,            ## the value ``true``
-    jsonFalse,           ## the value ``false``
-    jsonNull,            ## the value ``null``
-    jsonObjectStart,     ## start of an object: the ``{`` token
-    jsonObjectEnd,       ## end of an object: the ``}`` token
-    jsonArrayStart,      ## start of an array: the ``[`` token
-    jsonArrayEnd         ## start of an array: the ``]`` token
-
-  TokKind = enum         # must be synchronized with TJsonEventKind!
-    tkError,
-    tkEof,
-    tkString,
-    tkInt,
-    tkFloat,
-    tkTrue,
-    tkFalse,
-    tkNull,
-    tkCurlyLe,
-    tkCurlyRi,
-    tkBracketLe,
-    tkBracketRi,
-    tkColon,
-    tkComma
-
-  JsonError* = enum        ## enumeration that lists all errors that can occur
-    errNone,               ## no error
-    errInvalidToken,       ## invalid token
-    errStringExpected,     ## string expected
-    errColonExpected,      ## ``:`` expected
-    errCommaExpected,      ## ``,`` expected
-    errBracketRiExpected,  ## ``]`` expected
-    errCurlyRiExpected,    ## ``}`` expected
-    errQuoteExpected,      ## ``"`` or ``'`` expected
-    errEOC_Expected,       ## ``*/`` expected
-    errEofExpected,        ## EOF expected
-    errExprExpected        ## expr expected
-
-  ParserState = enum
-    stateEof, stateStart, stateObject, stateArray, stateExpectArrayComma,
-    stateExpectObjectComma, stateExpectColon, stateExpectValue
-
-  JsonParser* = object of BaseLexer ## the parser object.
-    a: string
-    tok: TokKind
-    kind: JsonEventKind
-    err: JsonError
-    state: seq[ParserState]
-    filename: string
-
-  JsonKindError* = object of ValueError ## raised by the ``to`` macro if the
-                                        ## JSON kind is incorrect.
-
-{.deprecated: [TJsonEventKind: JsonEventKind, TJsonError: JsonError,
-  TJsonParser: JsonParser, TTokKind: TokKind].}
-
-const
-  errorMessages: array[JsonError, string] = [
-    "no error",
-    "invalid token",
-    "string expected",
-    "':' expected",
-    "',' expected",
-    "']' expected",
-    "'}' expected",
-    "'\"' or \"'\" expected",
-    "'*/' expected",
-    "EOF expected",
-    "expression expected"
-  ]
-  tokToStr: array[TokKind, string] = [
-    "invalid token",
-    "EOF",
-    "string literal",
-    "int literal",
-    "float literal",
-    "true",
-    "false",
-    "null",
-    "{", "}", "[", "]", ":", ","
-  ]
-
-proc open*(my: var JsonParser, input: Stream, filename: string) =
-  ## initializes the parser with an input stream. `Filename` is only used
-  ## for nice error messages.
-  lexbase.open(my, input)
-  my.filename = filename
-  my.state = @[stateStart]
-  my.kind = jsonError
-  my.a = ""
-
-proc close*(my: var JsonParser) {.inline.} =
-  ## closes the parser `my` and its associated input stream.
-  lexbase.close(my)
-
-proc str*(my: JsonParser): string {.inline.} =
-  ## returns the character data for the events: ``jsonInt``, ``jsonFloat``,
-  ## ``jsonString``
-  assert(my.kind in {jsonInt, jsonFloat, jsonString})
-  return my.a
-
-proc getInt*(my: JsonParser): BiggestInt {.inline.} =
-  ## returns the number for the event: ``jsonInt``
-  assert(my.kind == jsonInt)
-  return parseBiggestInt(my.a)
-
-proc getFloat*(my: JsonParser): float {.inline.} =
-  ## returns the number for the event: ``jsonFloat``
-  assert(my.kind == jsonFloat)
-  return parseFloat(my.a)
-
-proc kind*(my: JsonParser): JsonEventKind {.inline.} =
-  ## returns the current event type for the JSON parser
-  return my.kind
-
-proc getColumn*(my: JsonParser): int {.inline.} =
-  ## get the current column the parser has arrived at.
-  result = getColNumber(my, my.bufpos)
-
-proc getLine*(my: JsonParser): int {.inline.} =
-  ## get the current line the parser has arrived at.
-  result = my.lineNumber
-
-proc getFilename*(my: JsonParser): string {.inline.} =
-  ## get the filename of the file that the parser processes.
-  result = my.filename
-
-proc errorMsg*(my: JsonParser): string =
-  ## returns a helpful error message for the event ``jsonError``
-  assert(my.kind == jsonError)
-  result = "$1($2, $3) Error: $4" % [
-    my.filename, $getLine(my), $getColumn(my), errorMessages[my.err]]
-
-proc errorMsgExpected*(my: JsonParser, e: string): string =
-  ## returns an error message "`e` expected" in the same format as the
-  ## other error messages
-  result = "$1($2, $3) Error: $4" % [
-    my.filename, $getLine(my), $getColumn(my), e & " expected"]
-
-proc handleHexChar(c: char, x: var int): bool =
-  result = true # Success
-  case c
-  of '0'..'9': x = (x shl 4) or (ord(c) - ord('0'))
-  of 'a'..'f': x = (x shl 4) or (ord(c) - ord('a') + 10)
-  of 'A'..'F': x = (x shl 4) or (ord(c) - ord('A') + 10)
-  else: result = false # error
-
-proc parseEscapedUTF16(buf: cstring, pos: var int): int =
-  result = 0
-  #UTF-16 escape is always 4 bytes.
-  for _ in 0..3:
-    if handleHexChar(buf[pos], result):
-      inc(pos)
-    else:
-      return -1
-
-proc parseString(my: var JsonParser): TokKind =
-  result = tkString
-  var pos = my.bufpos + 1
-  var buf = my.buf
-  while true:
-    case buf[pos]
-    of '\0':
-      my.err = errQuoteExpected
-      result = tkError
-      break
-    of '"':
-      inc(pos)
-      break
-    of '\\':
-      case buf[pos+1]
-      of '\\', '"', '\'', '/':
-        add(my.a, buf[pos+1])
-        inc(pos, 2)
-      of 'b':
-        add(my.a, '\b')
-        inc(pos, 2)
-      of 'f':
-        add(my.a, '\f')
-        inc(pos, 2)
-      of 'n':
-        add(my.a, '\L')
-        inc(pos, 2)
-      of 'r':
-        add(my.a, '\C')
-        inc(pos, 2)
-      of 't':
-        add(my.a, '\t')
-        inc(pos, 2)
-      of 'u':
-        inc(pos, 2)
-        var r = parseEscapedUTF16(buf, pos)
-        if r < 0:
-          my.err = errInvalidToken
-          break
-        # Deal with surrogates
-        if (r and 0xfc00) == 0xd800:
-          if buf[pos] & buf[pos+1] != "\\u":
-            my.err = errInvalidToken
-            break
-          inc(pos, 2)
-          var s = parseEscapedUTF16(buf, pos)
-          if (s and 0xfc00) == 0xdc00 and s > 0:
-            r = 0x10000 + (((r - 0xd800) shl 10) or (s - 0xdc00))
-          else:
-            my.err = errInvalidToken
-            break
-        add(my.a, toUTF8(Rune(r)))
-      else:
-        # don't bother with the error
-        add(my.a, buf[pos])
-        inc(pos)
-    of '\c':
-      pos = lexbase.handleCR(my, pos)
-      buf = my.buf
-      add(my.a, '\c')
-    of '\L':
-      pos = lexbase.handleLF(my, pos)
-      buf = my.buf
-      add(my.a, '\L')
-    else:
-      add(my.a, buf[pos])
-      inc(pos)
-  my.bufpos = pos # store back
-
-proc skip(my: var JsonParser) =
-  var pos = my.bufpos
-  var buf = my.buf
-  while true:
-    case buf[pos]
-    of '/':
-      if buf[pos+1] == '/':
-        # skip line comment:
-        inc(pos, 2)
-        while true:
-          case buf[pos]
-          of '\0':
-            break
-          of '\c':
-            pos = lexbase.handleCR(my, pos)
-            buf = my.buf
-            break
-          of '\L':
-            pos = lexbase.handleLF(my, pos)
-            buf = my.buf
-            break
-          else:
-            inc(pos)
-      elif buf[pos+1] == '*':
-        # skip long comment:
-        inc(pos, 2)
-        while true:
-          case buf[pos]
-          of '\0':
-            my.err = errEOC_Expected
-            break
-          of '\c':
-            pos = lexbase.handleCR(my, pos)
-            buf = my.buf
-          of '\L':
-            pos = lexbase.handleLF(my, pos)
-            buf = my.buf
-          of '*':
-            inc(pos)
-            if buf[pos] == '/':
-              inc(pos)
-              break
-          else:
-            inc(pos)
-      else:
-        break
-    of ' ', '\t':
-      inc(pos)
-    of '\c':
-      pos = lexbase.handleCR(my, pos)
-      buf = my.buf
-    of '\L':
-      pos = lexbase.handleLF(my, pos)
-      buf = my.buf
-    else:
-      break
-  my.bufpos = pos
-
-proc parseNumber(my: var JsonParser) =
-  var pos = my.bufpos
-  var buf = my.buf
-  if buf[pos] == '-':
-    add(my.a, '-')
-    inc(pos)
-  if buf[pos] == '.':
-    add(my.a, "0.")
-    inc(pos)
-  else:
-    while buf[pos] in Digits:
-      add(my.a, buf[pos])
-      inc(pos)
-    if buf[pos] == '.':
-      add(my.a, '.')
-      inc(pos)
-  # digits after the dot:
-  while buf[pos] in Digits:
-    add(my.a, buf[pos])
-    inc(pos)
-  if buf[pos] in {'E', 'e'}:
-    add(my.a, buf[pos])
-    inc(pos)
-    if buf[pos] in {'+', '-'}:
-      add(my.a, buf[pos])
-      inc(pos)
-    while buf[pos] in Digits:
-      add(my.a, buf[pos])
-      inc(pos)
-  my.bufpos = pos
-
-proc parseName(my: var JsonParser) =
-  var pos = my.bufpos
-  var buf = my.buf
-  if buf[pos] in IdentStartChars:
-    while buf[pos] in IdentChars:
-      add(my.a, buf[pos])
-      inc(pos)
-  my.bufpos = pos
-
-proc getTok(my: var JsonParser): TokKind =
-  setLen(my.a, 0)
-  skip(my) # skip whitespace, comments
-  case my.buf[my.bufpos]
-  of '-', '.', '0'..'9':
-    parseNumber(my)
-    if {'.', 'e', 'E'} in my.a:
-      result = tkFloat
-    else:
-      result = tkInt
-  of '"':
-    result = parseString(my)
-  of '[':
-    inc(my.bufpos)
-    result = tkBracketLe
-  of '{':
-    inc(my.bufpos)
-    result = tkCurlyLe
-  of ']':
-    inc(my.bufpos)
-    result = tkBracketRi
-  of '}':
-    inc(my.bufpos)
-    result = tkCurlyRi
-  of ',':
-    inc(my.bufpos)
-    result = tkComma
-  of ':':
-    inc(my.bufpos)
-    result = tkColon
-  of '\0':
-    result = tkEof
-  of 'a'..'z', 'A'..'Z', '_':
-    parseName(my)
-    case my.a
-    of "null": result = tkNull
-    of "true": result = tkTrue
-    of "false": result = tkFalse
-    else: result = tkError
-  else:
-    inc(my.bufpos)
-    result = tkError
-  my.tok = result
-
-proc next*(my: var JsonParser) =
-  ## retrieves the first/next event. This controls the parser.
-  var tk = getTok(my)
-  var i = my.state.len-1
-  # the following code is a state machine. If we had proper coroutines,
-  # the code could be much simpler.
-  case my.state[i]
-  of stateEof:
-    if tk == tkEof:
-      my.kind = jsonEof
-    else:
-      my.kind = jsonError
-      my.err = errEofExpected
-  of stateStart:
-    # tokens allowed?
-    case tk
-    of tkString, tkInt, tkFloat, tkTrue, tkFalse, tkNull:
-      my.state[i] = stateEof # expect EOF next!
-      my.kind = JsonEventKind(ord(tk))
-    of tkBracketLe:
-      my.state.add(stateArray) # we expect any
-      my.kind = jsonArrayStart
-    of tkCurlyLe:
-      my.state.add(stateObject)
-      my.kind = jsonObjectStart
-    of tkEof:
-      my.kind = jsonEof
-    else:
-      my.kind = jsonError
-      my.err = errEofExpected
-  of stateObject:
-    case tk
-    of tkString, tkInt, tkFloat, tkTrue, tkFalse, tkNull:
-      my.state.add(stateExpectColon)
-      my.kind = JsonEventKind(ord(tk))
-    of tkBracketLe:
-      my.state.add(stateExpectColon)
-      my.state.add(stateArray)
-      my.kind = jsonArrayStart
-    of tkCurlyLe:
-      my.state.add(stateExpectColon)
-      my.state.add(stateObject)
-      my.kind = jsonObjectStart
-    of tkCurlyRi:
-      my.kind = jsonObjectEnd
-      discard my.state.pop()
-    else:
-      my.kind = jsonError
-      my.err = errCurlyRiExpected
-  of stateArray:
-    case tk
-    of tkString, tkInt, tkFloat, tkTrue, tkFalse, tkNull:
-      my.state.add(stateExpectArrayComma) # expect value next!
-      my.kind = JsonEventKind(ord(tk))
-    of tkBracketLe:
-      my.state.add(stateExpectArrayComma)
-      my.state.add(stateArray)
-      my.kind = jsonArrayStart
-    of tkCurlyLe:
-      my.state.add(stateExpectArrayComma)
-      my.state.add(stateObject)
-      my.kind = jsonObjectStart
-    of tkBracketRi:
-      my.kind = jsonArrayEnd
-      discard my.state.pop()
-    else:
-      my.kind = jsonError
-      my.err = errBracketRiExpected
-  of stateExpectArrayComma:
-    case tk
-    of tkComma:
-      discard my.state.pop()
-      next(my)
-    of tkBracketRi:
-      my.kind = jsonArrayEnd
-      discard my.state.pop() # pop stateExpectArrayComma
-      discard my.state.pop() # pop stateArray
-    else:
-      my.kind = jsonError
-      my.err = errBracketRiExpected
-  of stateExpectObjectComma:
-    case tk
-    of tkComma:
-      discard my.state.pop()
-      next(my)
-    of tkCurlyRi:
-      my.kind = jsonObjectEnd
-      discard my.state.pop() # pop stateExpectObjectComma
-      discard my.state.pop() # pop stateObject
-    else:
-      my.kind = jsonError
-      my.err = errCurlyRiExpected
-  of stateExpectColon:
-    case tk
-    of tkColon:
-      my.state[i] = stateExpectValue
-      next(my)
-    else:
-      my.kind = jsonError
-      my.err = errColonExpected
-  of stateExpectValue:
-    case tk
-    of tkString, tkInt, tkFloat, tkTrue, tkFalse, tkNull:
-      my.state[i] = stateExpectObjectComma
-      my.kind = JsonEventKind(ord(tk))
-    of tkBracketLe:
-      my.state[i] = stateExpectObjectComma
-      my.state.add(stateArray)
-      my.kind = jsonArrayStart
-    of tkCurlyLe:
-      my.state[i] = stateExpectObjectComma
-      my.state.add(stateObject)
-      my.kind = jsonObjectStart
-    else:
-      my.kind = jsonError
-      my.err = errExprExpected
-
-
-# ------------- higher level interface ---------------------------------------
-
-type
   JsonNodeKind* = enum ## possible JSON node types
     JNull,
     JBool,
@@ -620,15 +132,6 @@ type
     of JArray:
       elems*: seq[JsonNode]
 
-  JsonParsingError* = object of ValueError ## is raised for a JSON error
-
-{.deprecated: [EJsonParsingError: JsonParsingError, TJsonNode: JsonNodeObj,
-    PJsonNode: JsonNode, TJsonNodeKind: JsonNodeKind].}
-
-proc raiseParseErr*(p: JsonParser, msg: string) {.noinline, noreturn.} =
-  ## raises an `EJsonParsingError` exception.
-  raise newException(JsonParsingError, errorMsgExpected(p, msg))
-
 proc newJString*(s: string): JsonNode =
   ## Creates a new `JString JsonNode`.
   new(result)
@@ -695,8 +198,8 @@ proc getBiggestInt*(n: JsonNode, default: BiggestInt = 0): BiggestInt =
   if n.isNil or n.kind != JInt: return default
   else: return n.num
 
-proc getNum*(n: JsonNode, default: BiggestInt = 0): BiggestInt {.deprecated.} =
-  ## Deprecated - use getInt or getBiggestInt instead
+proc getNum*(n: JsonNode, default: BiggestInt = 0): BiggestInt {.deprecated: "use getInt or getBiggestInt instead".} =
+  ## **Deprecated since v0.18.2:** use ``getInt`` or ``getBiggestInt`` instead.
   getBiggestInt(n, default)
 
 proc getFloat*(n: JsonNode, default: float = 0.0): float =
@@ -709,8 +212,8 @@ proc getFloat*(n: JsonNode, default: float = 0.0): float =
   of JInt: return float(n.num)
   else: return default
 
-proc getFNum*(n: JsonNode, default: float = 0.0): float {.deprecated.} =
-  ## Deprecated - use getFloat instead
+proc getFNum*(n: JsonNode, default: float = 0.0): float {.deprecated: "use getFloat instead".} =
+  ## **Deprecated since v0.18.2:** use ``getFloat`` instead.
   getFloat(n, default)
 
 proc getBool*(n: JsonNode, default: bool = false): bool =
@@ -720,8 +223,8 @@ proc getBool*(n: JsonNode, default: bool = false): bool =
   if n.isNil or n.kind != JBool: return default
   else: return n.bval
 
-proc getBVal*(n: JsonNode, default: bool = false): bool {.deprecated.} =
-  ## Deprecated - use getBVal instead
+proc getBVal*(n: JsonNode, default: bool = false): bool {.deprecated: "use getBool instead".} =
+  ## **Deprecated since v0.18.2:** use ``getBool`` instead.
   getBool(n, default)
 
 proc getFields*(n: JsonNode,
@@ -838,6 +341,9 @@ proc toJson(x: NimNode): NimNode {.compiletime.} =
     result = newCall(bindSym"newJObject")
   of nnkNilLit:
     result = newCall(bindSym"newJNull")
+  of nnkPar:
+    if x.len == 1: result = toJson(x[0])
+    else: result = newCall(bindSym"%", x)
   else:
     result = newCall(bindSym"%", x)
 
@@ -946,8 +452,8 @@ proc contains*(node: JsonNode, val: JsonNode): bool =
   assert(node.kind == JArray)
   find(node.elems, val) >= 0
 
-proc existsKey*(node: JsonNode, key: string): bool {.deprecated.} = node.hasKey(key)
-  ## Deprecated for `hasKey`
+proc existsKey*(node: JsonNode, key: string): bool {.deprecated: "use hasKey instead".} = node.hasKey(key)
+  ## **Deprecated:** use `hasKey` instead.
 
 proc `[]=`*(obj: JsonNode, key: string, val: JsonNode) {.inline.} =
   ## Sets a field from a `JObject`.
@@ -1197,10 +703,6 @@ iterator mpairs*(node: var JsonNode): tuple[key: string, val: var JsonNode] =
   for key, val in mpairs(node.fields):
     yield (key, val)
 
-proc eat(p: var JsonParser, tok: TokKind) =
-  if p.tok == tok: discard getTok(p)
-  else: raiseParseErr(p, tokToStr[tok])
-
 proc parseJson(p: var JsonParser): JsonNode =
   ## Parses JSON from a JSON Parser `p`.
   case p.tok
@@ -1256,10 +758,12 @@ when not defined(js):
     ## If `s` contains extra data, it will raise `JsonParsingError`.
     var p: JsonParser
     p.open(s, filename)
-    defer: p.close()
-    discard getTok(p) # read first token
-    result = p.parseJson()
-    eat(p, tkEof) # check if there is no extra data
+    try:
+      discard getTok(p) # read first token
+      result = p.parseJson()
+      eat(p, tkEof) # check if there is no extra data
+    finally:
+      p.close()
 
   proc parseJson*(buffer: string): JsonNode =
     ## Parses JSON from `buffer`.
@@ -1277,7 +781,6 @@ else:
   from math import `mod`
   type
     JSObject = object
-  {.deprecated: [TJSObject: JSObject].}
 
   proc parseNativeJson(x: cstring): JSObject {.importc: "JSON.parse".}
 
diff --git a/lib/pure/lexbase.nim b/lib/pure/lexbase.nim
index 15a390f0b..e38acd5ef 100644
--- a/lib/pure/lexbase.nim
+++ b/lib/pure/lexbase.nim
@@ -40,8 +40,6 @@ type
     offsetBase*: int          # use ``offsetBase + bufpos`` to get the offset
     refillChars: set[char]
 
-{.deprecated: [TBaseLexer: BaseLexer].}
-
 const
   chrSize = sizeof(char)
 
diff --git a/lib/pure/logging.nim b/lib/pure/logging.nim
index 751fc0e8d..cdff1f548 100644
--- a/lib/pure/logging.nim
+++ b/lib/pure/logging.nim
@@ -96,10 +96,6 @@ when not defined(js):
       logFiles: int # how many log files already created, e.g. basename.1, basename.2...
       bufSize: int # size of output buffer (-1: use system defaults, 0: unbuffered, >0: fixed buffer size)
 
-  {.deprecated: [PFileLogger: FileLogger, PRollingFileLogger: RollingFileLogger].}
-
-{.deprecated: [TLevel: Level, PLogger: Logger, PConsoleLogger: ConsoleLogger].}
-
 var
   level {.threadvar.}: Level   ## global log filter
   handlers {.threadvar.}: seq[Logger] ## handlers with their own log levels
@@ -126,7 +122,7 @@ proc substituteLog*(frmt: string, level: Level, args: varargs[string, `$`]): str
       var v = ""
       let app = when defined(js): "" else: getAppFilename()
       while frmt[i] in IdentChars:
-        v.add(toLower(frmt[i]))
+        v.add(toLowerAscii(frmt[i]))
         inc(i)
       case v
       of "date": result.add(getDateStr())
diff --git a/lib/pure/marshal.nim b/lib/pure/marshal.nim
index 6ee830786..b88c9dd85 100644
--- a/lib/pure/marshal.nim
+++ b/lib/pure/marshal.nim
@@ -309,7 +309,6 @@ when not defined(testing) and isMainModule:
     Node = object
       next, prev: PNode
       data: string
-  {.deprecated: [TNode: Node].}
 
   proc buildList(): PNode =
     new(result)
diff --git a/lib/pure/matchers.nim b/lib/pure/matchers.nim
index 6366fee1a..97223ed01 100644
--- a/lib/pure/matchers.nim
+++ b/lib/pure/matchers.nim
@@ -12,7 +12,7 @@
 ## **Warning:** This module is deprecated since version 0.14.0.
 {.deprecated.}
 
-{.deadCodeElim: on.}
+{.deadCodeElim: on.}  # dce option deprecated
 
 {.push debugger:off .} # the user does not want to trace a part
                        # of the standard library!
@@ -29,21 +29,21 @@ proc validEmailAddress*(s: string): bool {.noSideEffect,
     chars = Letters + Digits + {'!','#','$','%','&',
       '\'','*','+','/','=','?','^','_','`','{','}','|','~','-','.'}
   var i = 0
-  if s[i] notin chars or s[i] == '.': return false
-  while s[i] in chars:
-    if s[i] == '.' and s[i+1] == '.': return false
+  if i >= s.len or s[i] notin chars or s[i] == '.': return false
+  while i < s.len and s[i] in chars:
+    if i+1 < s.len and s[i] == '.' and s[i+1] == '.': return false
     inc(i)
-  if s[i] != '@': return false
+  if i >= s.len or s[i] != '@': return false
   var j = len(s)-1
-  if s[j] notin Letters: return false
+  if j >= 0 and s[j] notin Letters: return false
   while j >= i and s[j] in Letters: dec(j)
   inc(i) # skip '@'
-  while s[i] in {'0'..'9', 'a'..'z', '-', '.'}: inc(i)
-  if s[i] != '\0': return false
+  while i < s.len and s[i] in {'0'..'9', 'a'..'z', '-', '.'}: inc(i)
+  if i != s.len: return false
 
   var x = substr(s, j+1)
   if len(x) == 2 and x[0] in Letters and x[1] in Letters: return true
-  case toLower(x)
+  case toLowerAscii(x)
   of "com", "org", "net", "gov", "mil", "biz", "info", "mobi", "name",
      "aero", "jobs", "museum": return true
   else: return false
diff --git a/lib/pure/math.nim b/lib/pure/math.nim
index cbd04a145..9e590debc 100644
--- a/lib/pure/math.nim
+++ b/lib/pure/math.nim
@@ -29,11 +29,21 @@ proc binom*(n, k: int): int {.noSideEffect.} =
   for i in countup(2, k):
     result = (result * (n + 1 - i)) div i
 
-proc fac*(n: int): int {.noSideEffect.} =
+proc createFactTable[N: static[int]]: array[N, int] =
+  result[0] = 1
+  for i in 1 ..< N:
+    result[i] = result[i - 1] * i
+
+proc fac*(n: int): int =
   ## Computes the faculty/factorial function.
-  result = 1
-  for i in countup(2, n):
-    result = result * i
+  const factTable =
+    when sizeof(int) == 4:
+      createFactTable[13]()
+    else:
+      createFactTable[21]()
+  assert(n >= 0, $n & " must not be negative.")
+  assert(n < factTable.len, $n & " is too large to look up in the table")
+  factTable[n]
 
 {.push checks:off, line_dir:off, stack_trace:off.}
 
@@ -550,3 +560,14 @@ when isMainModule:
     assert sgn(Inf) == 1
     assert sgn(NaN) == 0
 
+  block: # fac() tests
+    try:
+      discard fac(-1)
+    except AssertionError:
+      discard
+
+    doAssert fac(0) == 1
+    doAssert fac(1) == 1
+    doAssert fac(2) == 2
+    doAssert fac(3) == 6
+    doAssert fac(4) == 24
diff --git a/lib/pure/memfiles.nim b/lib/pure/memfiles.nim
index 5c73381ff..bf6795b0c 100644
--- a/lib/pure/memfiles.nim
+++ b/lib/pure/memfiles.nim
@@ -38,8 +38,6 @@ type
     else:
       handle: cint
 
-{.deprecated: [TMemFile: MemFile].}
-
 proc mapMem*(m: var MemFile, mode: FileMode = fmRead,
              mappedSize = -1, offset = 0): pointer =
   ## returns a pointer to a mapped portion of MemFile `m`
diff --git a/lib/pure/mersenne.nim b/lib/pure/mersenne.nim
index 6ac0c4e56..b2227f114 100644
--- a/lib/pure/mersenne.nim
+++ b/lib/pure/mersenne.nim
@@ -12,8 +12,6 @@ type
     mt: array[0..623, uint32]
     index: int
 
-{.deprecated: [TMersenneTwister: MersenneTwister].}
-
 proc newMersenneTwister*(seed: uint32): MersenneTwister =
   result.index = 0
   result.mt[0] = seed
diff --git a/lib/pure/mimetypes.nim b/lib/pure/mimetypes.nim
index b397ef47b..ff69ba61e 100644
--- a/lib/pure/mimetypes.nim
+++ b/lib/pure/mimetypes.nim
@@ -13,8 +13,6 @@ type
   MimeDB* = object
     mimes: StringTableRef
 
-{.deprecated: [TMimeDB: MimeDB].}
-
 const mimes* = {
     "ez": "application/andrew-inset",
     "anx": "application/annodex",
diff --git a/lib/pure/nativesockets.nim b/lib/pure/nativesockets.nim
index 74b2c9741..5545ca2d1 100644
--- a/lib/pure/nativesockets.nim
+++ b/lib/pure/nativesockets.nim
@@ -85,9 +85,6 @@ type
     length*: int
     addrList*: seq[string]
 
-{.deprecated: [TPort: Port, TDomain: Domain, TType: SockType,
-    TProtocol: Protocol, TServent: Servent, THostent: Hostent].}
-
 when useWinVersion:
   let
     osInvalidSocket* = winlean.INVALID_SOCKET
@@ -616,8 +613,12 @@ proc setBlocking*(s: SocketHandle, blocking: bool) =
 proc timeValFromMilliseconds(timeout = 500): Timeval =
   if timeout != -1:
     var seconds = timeout div 1000
-    result.tv_sec = seconds.int32
-    result.tv_usec = ((timeout - seconds * 1000) * 1000).int32
+    when useWinVersion:
+      result.tv_sec = seconds.int32
+      result.tv_usec = ((timeout - seconds * 1000) * 1000).int32
+    else:
+      result.tv_sec = seconds.Time
+      result.tv_usec = ((timeout - seconds * 1000) * 1000).Suseconds
 
 proc createFdSet(fd: var TFdSet, s: seq[SocketHandle], m: var int) =
   FD_ZERO(fd)
@@ -636,7 +637,7 @@ proc pruneSocketSet(s: var seq[SocketHandle], fd: var TFdSet) =
       inc(i)
   setLen(s, L)
 
-proc select*(readfds: var seq[SocketHandle], timeout = 500): int {.deprecated.} =
+proc select*(readfds: var seq[SocketHandle], timeout = 500): int {.deprecated: "use selectRead instead".} =
   ## When a socket in ``readfds`` is ready to be read from then a non-zero
   ## value will be returned specifying the count of the sockets which can be
   ## read from. The sockets which can be read from will also be removed
diff --git a/lib/pure/net.nim b/lib/pure/net.nim
index ccf02a1fc..fc04ef1af 100644
--- a/lib/pure/net.nim
+++ b/lib/pure/net.nim
@@ -68,7 +68,7 @@
 ## ``newSocket()``. The difference is that the latter creates a new file
 ## descriptor.
 
-{.deadCodeElim: on.}
+{.deadCodeElim: on.}  # dce option deprecated
 import nativesockets, os, strutils, parseutils, times, sets, options
 export Port, `$`, `==`
 export Domain, SockType, Protocol
@@ -110,9 +110,6 @@ when defineSsl:
       serverGetPskFunc: SslServerGetPskFunc
       clientGetPskFunc: SslClientGetPskFunc
 
-  {.deprecated: [ESSL: SSLError, TSSLCVerifyMode: SSLCVerifyMode,
-    TSSLProtVersion: SSLProtVersion, PSSLContext: SSLContext,
-    TSSLAcceptResult: SSLAcceptResult].}
 else:
   type
     SslContext* = void # TODO: Workaround #4797.
@@ -159,10 +156,6 @@ type
     Peek,
     SafeDisconn ## Ensures disconnection exceptions (ECONNRESET, EPIPE etc) are not thrown.
 
-{.deprecated: [TSocketFlags: SocketFlag, ETimeout: TimeoutError,
-    TReadLineResult: ReadLineResult, TSOBool: SOBool, PSocket: Socket,
-    TSocketImpl: SocketImpl].}
-
 type
   IpAddressFamily* {.pure.} = enum ## Describes the type of an IP address
     IPv6, ## IPv6 address
@@ -176,8 +169,6 @@ type
     of IpAddressFamily.IPv4:
       address_v4*: array[0..3, uint8] ## Contains the IP address in bytes in
                                       ## case of IPv4
-{.deprecated: [TIpAddress: IpAddress].}
-
 
 proc socketError*(socket: Socket, err: int = -1, async = false,
                   lastError = (-1).OSErrorCode): void {.gcsafe.}
@@ -969,7 +960,7 @@ when defined(posix) and not defined(nimdoc):
       raise newException(ValueError, "socket path too long")
     copyMem(addr result.sun_path, path.cstring, path.len + 1)
 
-when defined(posix):
+when defined(posix) or defined(nimdoc):
   proc connectUnix*(socket: Socket, path: string) =
     ## Connects to Unix socket on `path`.
     ## This only works on Unix-style systems: Mac OS X, BSD and Linux
@@ -1421,8 +1412,7 @@ proc sendTo*(socket: Socket, address: string, port: Port, data: pointer,
   ##
   ## **Note:** This proc is not available for SSL sockets.
   assert(not socket.isClosed, "Cannot `sendTo` on a closed socket")
-  var aiList = getAddrInfo(address, port, af)
-
+  var aiList = getAddrInfo(address, port, af, socket.sockType, socket.protocol)
   # try all possibilities:
   var success = false
   var it = aiList
@@ -1443,7 +1433,7 @@ proc sendTo*(socket: Socket, address: string, port: Port,
   ## this function will try each IP of that hostname.
   ##
   ## This is the high-level version of the above ``sendTo`` function.
-  result = socket.sendTo(address, port, cstring(data), data.len)
+  result = socket.sendTo(address, port, cstring(data), data.len, socket.domain )
 
 
 proc isSsl*(socket: Socket): bool =
diff --git a/lib/pure/nimprof.nim b/lib/pure/nimprof.nim
index 4289eb049..506c6bfaa 100644
--- a/lib/pure/nimprof.nim
+++ b/lib/pure/nimprof.nim
@@ -30,7 +30,6 @@ when not declared(system.StackTrace):
   type StackTrace = object
     lines: array[0..20, cstring]
     files: array[0..20, cstring]
-  {.deprecated: [TStackTrace: StackTrace].}
   proc `[]`*(st: StackTrace, i: int): cstring = st.lines[i]
 
 # We use a simple hash table of bounded size to keep track of the stack traces:
@@ -39,7 +38,6 @@ type
     total: int
     st: StackTrace
   ProfileData = array[0..64*1024-1, ptr ProfileEntry]
-{.deprecated: [TProfileEntry: ProfileEntry, TProfileData: ProfileData].}
 
 proc `==`(a, b: StackTrace): bool =
   for i in 0 .. high(a.lines):
diff --git a/lib/pure/oids.nim b/lib/pure/oids.nim
index 26a55de6c..d6369b5f9 100644
--- a/lib/pure/oids.nim
+++ b/lib/pure/oids.nim
@@ -23,11 +23,9 @@ type
     fuzz: int32  ##
     count: int32 ##
 
-{.deprecated: [Toid: Oid].}
-
 proc `==`*(oid1: Oid, oid2: Oid): bool =
-    ## Compare two Mongo Object IDs for equality
-    return (oid1.time == oid2.time) and (oid1.fuzz == oid2.fuzz) and (oid1.count == oid2.count)
+  ## Compare two Mongo Object IDs for equality
+  return (oid1.time == oid2.time) and (oid1.fuzz == oid2.fuzz) and (oid1.count == oid2.count)
 
 proc hexbyte*(hex: char): int =
   case hex
diff --git a/lib/pure/options.nim b/lib/pure/options.nim
index 8a771be71..bd01b208a 100644
--- a/lib/pure/options.nim
+++ b/lib/pure/options.nim
@@ -104,6 +104,10 @@ proc none*(T: typedesc): Option[T] =
   # the default is the none type
   discard
 
+proc none*[T]: Option[T] =
+  ## Alias for ``none(T)``.
+  none(T)
+
 proc isSome*[T](self: Option[T]): bool {.inline.} =
   when T is SomePointer:
     self.val != nil
@@ -130,7 +134,7 @@ proc get*[T](self: Option[T]): T =
 
 proc get*[T](self: Option[T], otherwise: T): T =
   ## Returns the contents of this option or `otherwise` if the option is none.
-  if self.has:
+  if self.isSome:
     self.val
   else:
     otherwise
@@ -150,7 +154,7 @@ proc map*[T, R](self: Option[T], callback: proc (input: T): R): Option[R] =
 
 proc flatten*[A](self: Option[Option[A]]): Option[A] =
   ## Remove one level of structure in a nested Option.
-  if self.has:
+  if self.isSome:
     self.val
   else:
     none(A)
@@ -175,14 +179,14 @@ proc filter*[T](self: Option[T], callback: proc (input: T): bool): Option[T] =
 proc `==`*(a, b: Option): bool =
   ## Returns ``true`` if both ``Option``s are ``none``,
   ## or if they have equal values
-  (a.has and b.has and a.val == b.val) or (not a.has and not b.has)
+  (a.isSome and b.isSome and a.val == b.val) or (not a.isSome and not b.isSome)
 
 proc `$`*[T](self: Option[T]): string =
   ## Get the string representation of this option. If the option has a value,
   ## the result will be `Some(x)` where `x` is the string representation of the contained value.
   ## If the option does not have a value, the result will be `None[T]` where `T` is the name of
   ## the type contained in the option.
-  if self.has:
+  if self.isSome:
     "Some(" & $self.val & ")"
   else:
     "None[" & T.name & "]"
@@ -290,3 +294,7 @@ when isMainModule:
 
       let tmp = option(intref)
       check(sizeof(tmp) == sizeof(ptr int))
+
+    test "none[T]":
+      check(none[int]().isNone)
+      check(none(int) == none[int]())
diff --git a/lib/pure/os.nim b/lib/pure/os.nim
index a77bee99d..03445a035 100644
--- a/lib/pure/os.nim
+++ b/lib/pure/os.nim
@@ -10,7 +10,7 @@
 ## This module contains basic operating system facilities like
 ## retrieving environment variables, reading command line arguments,
 ## working with directories, running shell commands, etc.
-{.deadCodeElim: on.}
+{.deadCodeElim: on.}  # dce option deprecated
 
 {.push debugger: off.}
 
@@ -188,7 +188,7 @@ proc getLastModificationTime*(file: string): times.Time {.rtl, extern: "nos$1".}
     var f: WIN32_FIND_DATA
     var h = findFirstFile(file, f)
     if h == -1'i32: raiseOSError(osLastError())
-    result = fromUnix(winTimeToUnixTime(rdFileTime(f.ftLastWriteTime)).int64)
+    result = fromWinTime(rdFileTime(f.ftLastWriteTime))
     findClose(h)
 
 proc getLastAccessTime*(file: string): times.Time {.rtl, extern: "nos$1".} =
@@ -201,7 +201,7 @@ proc getLastAccessTime*(file: string): times.Time {.rtl, extern: "nos$1".} =
     var f: WIN32_FIND_DATA
     var h = findFirstFile(file, f)
     if h == -1'i32: raiseOSError(osLastError())
-    result = fromUnix(winTimeToUnixTime(rdFileTime(f.ftLastAccessTime)).int64)
+    result = fromWinTime(rdFileTime(f.ftLastAccessTime))
     findClose(h)
 
 proc getCreationTime*(file: string): times.Time {.rtl, extern: "nos$1".} =
@@ -218,7 +218,7 @@ proc getCreationTime*(file: string): times.Time {.rtl, extern: "nos$1".} =
     var f: WIN32_FIND_DATA
     var h = findFirstFile(file, f)
     if h == -1'i32: raiseOSError(osLastError())
-    result = fromUnix(winTimeToUnixTime(rdFileTime(f.ftCreationTime)).int64)
+    result = fromWinTime(rdFileTime(f.ftCreationTime))
     findClose(h)
 
 proc fileNewer*(a, b: string): bool {.rtl, extern: "nos$1".} =
@@ -329,20 +329,21 @@ proc expandFilename*(filename: string): string {.rtl, extern: "nos$1",
       c_free(cast[pointer](r))
 
 when defined(Windows):
-  proc openHandle(path: string, followSymlink=true): Handle =
+  proc openHandle(path: string, followSymlink=true, writeAccess=false): Handle =
     var flags = FILE_FLAG_BACKUP_SEMANTICS or FILE_ATTRIBUTE_NORMAL
     if not followSymlink:
       flags = flags or FILE_FLAG_OPEN_REPARSE_POINT
+    let access = if writeAccess: GENERIC_WRITE else: 0'i32
 
     when useWinUnicode:
       result = createFileW(
-        newWideCString(path), 0'i32,
+        newWideCString(path), access,
         FILE_SHARE_DELETE or FILE_SHARE_READ or FILE_SHARE_WRITE,
         nil, OPEN_EXISTING, flags, 0
         )
     else:
       result = createFileA(
-        path, 0'i32,
+        path, access,
         FILE_SHARE_DELETE or FILE_SHARE_READ or FILE_SHARE_WRITE,
         nil, OPEN_EXISTING, flags, 0
         )
@@ -430,8 +431,6 @@ type
     fpOthersWrite,         ## write access for others
     fpOthersRead           ## read access for others
 
-{.deprecated: [TFilePermission: FilePermission].}
-
 proc getFilePermissions*(filename: string): set[FilePermission] {.
   rtl, extern: "nos$1", tags: [ReadDirEffect].} =
   ## retrieves file permissions for `filename`. `OSError` is raised in case of
@@ -729,8 +728,6 @@ type
     pcDir,                ## path refers to a directory
     pcLinkToDir           ## path refers to a symbolic link to a directory
 
-{.deprecated: [TPathComponent: PathComponent].}
-
 
 when defined(posix):
   proc getSymlinkFileKind(path: string): PathComponent =
@@ -1059,18 +1056,17 @@ proc parseCmdLine*(c: string): seq[string] {.
   while true:
     setLen(a, 0)
     # eat all delimiting whitespace
-    while c[i] == ' ' or c[i] == '\t' or c[i] == '\l' or c[i] == '\r' : inc(i)
+    while i < c.len and c[i] in {' ', '\t', '\l', '\r'}: inc(i)
+    if i >= c.len: break
     when defined(windows):
       # parse a single argument according to the above rules:
-      if c[i] == '\0': break
       var inQuote = false
-      while true:
+      while i < c.len:
         case c[i]
-        of '\0': break
         of '\\':
           var j = i
-          while c[j] == '\\': inc(j)
-          if c[j] == '"':
+          while j < c.len and c[j] == '\\': inc(j)
+          if j < c.len and c[j] == '"':
             for k in 1..(j-i) div 2: a.add('\\')
             if (j-i) mod 2 == 0:
               i = j
@@ -1083,7 +1079,7 @@ proc parseCmdLine*(c: string): seq[string] {.
         of '"':
           inc(i)
           if not inQuote: inQuote = true
-          elif c[i] == '"':
+          elif i < c.len and c[i] == '"':
             a.add(c[i])
             inc(i)
           else:
@@ -1101,13 +1097,12 @@ proc parseCmdLine*(c: string): seq[string] {.
       of '\'', '\"':
         var delim = c[i]
         inc(i) # skip ' or "
-        while c[i] != '\0' and c[i] != delim:
+        while i < c.len and c[i] != delim:
           add a, c[i]
           inc(i)
-        if c[i] != '\0': inc(i)
-      of '\0': break
+        if i < c.len: inc(i)
       else:
-        while c[i] > ' ':
+        while i < c.len and c[i] > ' ':
           add(a, c[i])
           inc(i)
     add(result, a)
@@ -1442,18 +1437,6 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect].} =
     if result.len == 0:
       result = getApplHeuristic()
 
-proc getApplicationFilename*(): string {.rtl, extern: "nos$1", deprecated.} =
-  ## Returns the filename of the application's executable.
-  ## **Deprecated since version 0.8.12**: use ``getAppFilename``
-  ## instead.
-  result = getAppFilename()
-
-proc getApplicationDir*(): string {.rtl, extern: "nos$1", deprecated.} =
-  ## Returns the directory of the application's executable.
-  ## **Deprecated since version 0.8.12**: use ``getAppDir``
-  ## instead.
-  result = splitFile(getAppFilename()).dir
-
 proc getAppDir*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect].} =
   ## Returns the directory of the application's executable.
   result = splitFile(getAppFilename()).dir
@@ -1511,16 +1494,14 @@ template rawToFormalFileInfo(rawInfo, path, formalInfo): untyped =
   ## 'rawInfo' is either a 'TBY_HANDLE_FILE_INFORMATION' structure on Windows,
   ## or a 'Stat' structure on posix
   when defined(Windows):
-    template toTime(e: FILETIME): untyped {.gensym.} =
-      fromUnix(winTimeToUnixTime(rdFileTime(e)).int64) # local templates default to bind semantics
     template merge(a, b): untyped = a or (b shl 32)
     formalInfo.id.device = rawInfo.dwVolumeSerialNumber
     formalInfo.id.file = merge(rawInfo.nFileIndexLow, rawInfo.nFileIndexHigh)
     formalInfo.size = merge(rawInfo.nFileSizeLow, rawInfo.nFileSizeHigh)
     formalInfo.linkCount = rawInfo.nNumberOfLinks
-    formalInfo.lastAccessTime = toTime(rawInfo.ftLastAccessTime)
-    formalInfo.lastWriteTime = toTime(rawInfo.ftLastWriteTime)
-    formalInfo.creationTime = toTime(rawInfo.ftCreationTime)
+    formalInfo.lastAccessTime = fromWinTime(rdFileTime(rawInfo.ftLastAccessTime))
+    formalInfo.lastWriteTime = fromWinTime(rdFileTime(rawInfo.ftLastWriteTime))
+    formalInfo.creationTime = fromWinTime(rdFileTime(rawInfo.ftCreationTime))
 
     # Retrieve basic permissions
     if (rawInfo.dwFileAttributes and FILE_ATTRIBUTE_READONLY) != 0'i32:
@@ -1654,3 +1635,18 @@ proc isHidden*(path: string): bool =
         result = (fileName[0] == '.') and (fileName[3] != '.')
 
 {.pop.}
+
+proc setLastModificationTime*(file: string, t: times.Time) =
+  ## Sets the `file`'s last modification time. `OSError` is raised in case of
+  ## an error.
+  when defined(posix):
+    let unixt = posix.Time(t.toUnix)
+    var timevals = [Timeval(tv_sec: unixt), Timeval(tv_sec: unixt)] # [last access, last modification]
+    if utimes(file, timevals.addr) != 0: raiseOSError(osLastError())
+  else:
+    let h = openHandle(path = file, writeAccess = true)
+    if h == INVALID_HANDLE_VALUE: raiseOSError(osLastError())
+    var ft = t.toWinTime.toFILETIME
+    let res = setFileTime(h, nil, nil, ft.addr)
+    discard h.closeHandle
+    if res == 0'i32: raiseOSError(osLastError())
\ No newline at end of file
diff --git a/lib/pure/ospaths.nim b/lib/pure/ospaths.nim
index 0d638abb9..c0d3ef512 100644
--- a/lib/pure/ospaths.nim
+++ b/lib/pure/ospaths.nim
@@ -29,11 +29,6 @@ type
 
   OSErrorCode* = distinct int32 ## Specifies an OS Error Code.
 
-{.deprecated: [FReadEnv: ReadEnvEffect, FWriteEnv: WriteEnvEffect,
-    FReadDir: ReadDirEffect,
-    FWriteDir: WriteDirEffect,
-    TOSErrorCode: OSErrorCode
-].}
 const
   doslikeFileSystem* = defined(windows) or defined(OS2) or defined(DOS)
 
@@ -196,7 +191,7 @@ proc joinPath*(head, tail: string): string {.
     else:
       result = head & tail
   else:
-    if tail[0] in {DirSep, AltSep}:
+    if tail.len > 0 and tail[0] in {DirSep, AltSep}:
       result = head & tail
     else:
       result = head & DirSep & tail
@@ -477,7 +472,7 @@ proc unixToNativePath*(path: string, drive=""): string {.
 
     var i = start
     while i < len(path): # ../../../ --> ::::
-      if path[i] == '.' and path[i+1] == '.' and path[i+2] == '/':
+      if i+2 < path.len and path[i] == '.' and path[i+1] == '.' and path[i+2] == '/':
         # parent directory
         when defined(macos):
           if result[high(result)] == ':':
diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim
index 555626514..664446d54 100644
--- a/lib/pure/osproc.nim
+++ b/lib/pure/osproc.nim
@@ -61,9 +61,6 @@ type
   Process* = ref ProcessObj ## represents an operating system process
 
 
-{.deprecated: [TProcess: ProcessObj, PProcess: Process,
-  TProcessOption: ProcessOption].}
-
 const poUseShell* {.deprecated.} = poUsePath
   ## Deprecated alias for poUsePath.
 
@@ -373,17 +370,15 @@ template streamAccess(p) =
 when defined(Windows) and not defined(useNimRtl):
   # We need to implement a handle stream for Windows:
   type
-    PFileHandleStream = ref FileHandleStream
-    FileHandleStream = object of StreamObj
+    FileHandleStream = ref object of StreamObj
       handle: Handle
       atTheEnd: bool
-  {.deprecated: [TFileHandleStream: FileHandleStream].}
 
   proc hsClose(s: Stream) = discard # nothing to do here
-  proc hsAtEnd(s: Stream): bool = return PFileHandleStream(s).atTheEnd
+  proc hsAtEnd(s: Stream): bool = return FileHandleStream(s).atTheEnd
 
   proc hsReadData(s: Stream, buffer: pointer, bufLen: int): int =
-    var s = PFileHandleStream(s)
+    var s = FileHandleStream(s)
     if s.atTheEnd: return 0
     var br: int32
     var a = winlean.readFile(s.handle, buffer, bufLen.cint, addr br, nil)
@@ -395,13 +390,13 @@ when defined(Windows) and not defined(useNimRtl):
     result = br
 
   proc hsWriteData(s: Stream, buffer: pointer, bufLen: int) =
-    var s = PFileHandleStream(s)
+    var s = FileHandleStream(s)
     var bytesWritten: int32
     var a = winlean.writeFile(s.handle, buffer, bufLen.cint,
                               addr bytesWritten, nil)
     if a == 0: raiseOSError(osLastError())
 
-  proc newFileHandleStream(handle: Handle): PFileHandleStream =
+  proc newFileHandleStream(handle: Handle): FileHandleStream =
     new(result)
     result.handle = handle
     result.closeImpl = hsClose
@@ -527,7 +522,7 @@ when defined(Windows) and not defined(useNimRtl):
           if setHandleInformation(he, DWORD(1), DWORD(0)) == 0'i32:
             raiseOsError(osLastError())
         if setHandleInformation(hi, DWORD(1), DWORD(0)) == 0'i32:
-          raiseOsError(osLastError())  
+          raiseOsError(osLastError())
         if setHandleInformation(ho, DWORD(1), DWORD(0)) == 0'i32:
           raiseOsError(osLastError())
       else:
@@ -752,14 +747,14 @@ elif not defined(useNimRtl):
       copyMem(result[i], addr(x[0]), x.len+1)
       inc(i)
 
-  type StartProcessData = object
-    sysCommand: string
-    sysArgs: cstringArray
-    sysEnv: cstringArray
-    workingDir: cstring
-    pStdin, pStdout, pStderr, pErrorPipe: array[0..1, cint]
-    options: set[ProcessOption]
-  {.deprecated: [TStartProcessData: StartProcessData].}
+  type
+    StartProcessData = object
+      sysCommand: string
+      sysArgs: cstringArray
+      sysEnv: cstringArray
+      workingDir: cstring
+      pStdin, pStdout, pStderr, pErrorPipe: array[0..1, cint]
+      options: set[ProcessOption]
 
   const useProcessAuxSpawn = declared(posix_spawn) and not defined(useFork) and
                              not defined(useClone) and not defined(linux)
@@ -1001,13 +996,21 @@ elif not defined(useNimRtl):
   {.pop}
 
   proc close(p: Process) =
-    if p.inStream != nil: close(p.inStream)
-    if p.outStream != nil: close(p.outStream)
-    if p.errStream != nil: close(p.errStream)
     if poParentStreams notin p.options:
-      discard close(p.inHandle)
-      discard close(p.outHandle)
-      discard close(p.errHandle)
+      if p.inStream != nil:
+        close(p.inStream)
+      else:
+        discard close(p.inHandle)
+
+      if p.outStream != nil:
+        close(p.outStream)
+      else:
+        discard close(p.outHandle)
+
+      if p.errStream != nil:
+        close(p.errStream)
+      else:
+        discard close(p.errHandle)
 
   proc suspend(p: Process) =
     if kill(p.id, SIGSTOP) != 0'i32: raiseOsError(osLastError())
@@ -1281,7 +1284,7 @@ elif not defined(useNimRtl):
 
   proc select(readfds: var seq[Process], timeout = 500): int =
     var tv: Timeval
-    tv.tv_sec = 0
+    tv.tv_sec = posix.Time(0)
     tv.tv_usec = timeout * 1000
 
     var rd: TFdSet
diff --git a/lib/pure/parsecfg.nim b/lib/pure/parsecfg.nim
index 2a5dbc8f8..5fa2d8dc3 100644
--- a/lib/pure/parsecfg.nim
+++ b/lib/pure/parsecfg.nim
@@ -125,9 +125,6 @@ type
     tok: Token
     filename: string
 
-{.deprecated: [TCfgEventKind: CfgEventKind, TCfgEvent: CfgEvent,
-    TTokKind: TokKind, TToken: Token, TCfgParser: CfgParser].}
-
 # implementation
 
 const
diff --git a/lib/pure/parsecsv.nim b/lib/pure/parsecsv.nim
index 358ce829d..796114d37 100644
--- a/lib/pure/parsecsv.nim
+++ b/lib/pure/parsecsv.nim
@@ -66,8 +66,6 @@ type
   CsvError* = object of IOError ## exception that is raised if
                                 ## a parsing error occurs
 
-{.deprecated: [TCsvRow: CsvRow, TCsvParser: CsvParser, EInvalidCsv: CsvError].}
-
 proc raiseEInvalidCsv(filename: string, line, col: int,
                       msg: string) {.noreturn.} =
   var e: ref CsvError
diff --git a/lib/pure/parsejson.nim b/lib/pure/parsejson.nim
new file mode 100644
index 000000000..9c53af6a6
--- /dev/null
+++ b/lib/pure/parsejson.nim
@@ -0,0 +1,535 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2018 Nim contributors
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module implements a json parser. It is used
+## and exported by the ``json`` standard library
+## module, but can also be used in its own right.
+
+import
+  strutils, lexbase, streams, unicode
+
+type
+  JsonEventKind* = enum  ## enumeration of all events that may occur when parsing
+    jsonError,           ## an error occurred during parsing
+    jsonEof,             ## end of file reached
+    jsonString,          ## a string literal
+    jsonInt,             ## an integer literal
+    jsonFloat,           ## a float literal
+    jsonTrue,            ## the value ``true``
+    jsonFalse,           ## the value ``false``
+    jsonNull,            ## the value ``null``
+    jsonObjectStart,     ## start of an object: the ``{`` token
+    jsonObjectEnd,       ## end of an object: the ``}`` token
+    jsonArrayStart,      ## start of an array: the ``[`` token
+    jsonArrayEnd         ## start of an array: the ``]`` token
+
+  TokKind* = enum         # must be synchronized with TJsonEventKind!
+    tkError,
+    tkEof,
+    tkString,
+    tkInt,
+    tkFloat,
+    tkTrue,
+    tkFalse,
+    tkNull,
+    tkCurlyLe,
+    tkCurlyRi,
+    tkBracketLe,
+    tkBracketRi,
+    tkColon,
+    tkComma
+
+  JsonError* = enum        ## enumeration that lists all errors that can occur
+    errNone,               ## no error
+    errInvalidToken,       ## invalid token
+    errStringExpected,     ## string expected
+    errColonExpected,      ## ``:`` expected
+    errCommaExpected,      ## ``,`` expected
+    errBracketRiExpected,  ## ``]`` expected
+    errCurlyRiExpected,    ## ``}`` expected
+    errQuoteExpected,      ## ``"`` or ``'`` expected
+    errEOC_Expected,       ## ``*/`` expected
+    errEofExpected,        ## EOF expected
+    errExprExpected        ## expr expected
+
+  ParserState = enum
+    stateEof, stateStart, stateObject, stateArray, stateExpectArrayComma,
+    stateExpectObjectComma, stateExpectColon, stateExpectValue
+
+  JsonParser* = object of BaseLexer ## the parser object.
+    a*: string
+    tok*: TokKind
+    kind: JsonEventKind
+    err: JsonError
+    state: seq[ParserState]
+    filename: string
+    rawStringLiterals: bool
+
+  JsonKindError* = object of ValueError ## raised by the ``to`` macro if the
+                                        ## JSON kind is incorrect.
+  JsonParsingError* = object of ValueError ## is raised for a JSON error
+
+const
+  errorMessages*: array[JsonError, string] = [
+    "no error",
+    "invalid token",
+    "string expected",
+    "':' expected",
+    "',' expected",
+    "']' expected",
+    "'}' expected",
+    "'\"' or \"'\" expected",
+    "'*/' expected",
+    "EOF expected",
+    "expression expected"
+  ]
+  tokToStr: array[TokKind, string] = [
+    "invalid token",
+    "EOF",
+    "string literal",
+    "int literal",
+    "float literal",
+    "true",
+    "false",
+    "null",
+    "{", "}", "[", "]", ":", ","
+  ]
+
+proc open*(my: var JsonParser, input: Stream, filename: string;
+           rawStringLiterals = false) =
+  ## initializes the parser with an input stream. `Filename` is only used
+  ## for nice error messages. If `rawStringLiterals` is true, string literals
+  ## are kepts with their surrounding quotes and escape sequences in them are
+  ## left untouched too.
+  lexbase.open(my, input)
+  my.filename = filename
+  my.state = @[stateStart]
+  my.kind = jsonError
+  my.a = ""
+  my.rawStringLiterals = rawStringLiterals
+
+proc close*(my: var JsonParser) {.inline.} =
+  ## closes the parser `my` and its associated input stream.
+  lexbase.close(my)
+
+proc str*(my: JsonParser): string {.inline.} =
+  ## returns the character data for the events: ``jsonInt``, ``jsonFloat``,
+  ## ``jsonString``
+  assert(my.kind in {jsonInt, jsonFloat, jsonString})
+  return my.a
+
+proc getInt*(my: JsonParser): BiggestInt {.inline.} =
+  ## returns the number for the event: ``jsonInt``
+  assert(my.kind == jsonInt)
+  return parseBiggestInt(my.a)
+
+proc getFloat*(my: JsonParser): float {.inline.} =
+  ## returns the number for the event: ``jsonFloat``
+  assert(my.kind == jsonFloat)
+  return parseFloat(my.a)
+
+proc kind*(my: JsonParser): JsonEventKind {.inline.} =
+  ## returns the current event type for the JSON parser
+  return my.kind
+
+proc getColumn*(my: JsonParser): int {.inline.} =
+  ## get the current column the parser has arrived at.
+  result = getColNumber(my, my.bufpos)
+
+proc getLine*(my: JsonParser): int {.inline.} =
+  ## get the current line the parser has arrived at.
+  result = my.lineNumber
+
+proc getFilename*(my: JsonParser): string {.inline.} =
+  ## get the filename of the file that the parser processes.
+  result = my.filename
+
+proc errorMsg*(my: JsonParser): string =
+  ## returns a helpful error message for the event ``jsonError``
+  assert(my.kind == jsonError)
+  result = "$1($2, $3) Error: $4" % [
+    my.filename, $getLine(my), $getColumn(my), errorMessages[my.err]]
+
+proc errorMsgExpected*(my: JsonParser, e: string): string =
+  ## returns an error message "`e` expected" in the same format as the
+  ## other error messages
+  result = "$1($2, $3) Error: $4" % [
+    my.filename, $getLine(my), $getColumn(my), e & " expected"]
+
+proc handleHexChar(c: char, x: var int): bool =
+  result = true # Success
+  case c
+  of '0'..'9': x = (x shl 4) or (ord(c) - ord('0'))
+  of 'a'..'f': x = (x shl 4) or (ord(c) - ord('a') + 10)
+  of 'A'..'F': x = (x shl 4) or (ord(c) - ord('A') + 10)
+  else: result = false # error
+
+proc parseEscapedUTF16*(buf: cstring, pos: var int): int =
+  result = 0
+  #UTF-16 escape is always 4 bytes.
+  for _ in 0..3:
+    if handleHexChar(buf[pos], result):
+      inc(pos)
+    else:
+      return -1
+
+proc parseString(my: var JsonParser): TokKind =
+  result = tkString
+  var pos = my.bufpos + 1
+  var buf = my.buf
+  if my.rawStringLiterals:
+    add(my.a, '"')
+  while true:
+    case buf[pos]
+    of '\0':
+      my.err = errQuoteExpected
+      result = tkError
+      break
+    of '"':
+      if my.rawStringLiterals:
+        add(my.a, '"')
+      inc(pos)
+      break
+    of '\\':
+      if my.rawStringLiterals:
+        add(my.a, '\\')
+      case buf[pos+1]
+      of '\\', '"', '\'', '/':
+        add(my.a, buf[pos+1])
+        inc(pos, 2)
+      of 'b':
+        add(my.a, '\b')
+        inc(pos, 2)
+      of 'f':
+        add(my.a, '\f')
+        inc(pos, 2)
+      of 'n':
+        add(my.a, '\L')
+        inc(pos, 2)
+      of 'r':
+        add(my.a, '\C')
+        inc(pos, 2)
+      of 't':
+        add(my.a, '\t')
+        inc(pos, 2)
+      of 'u':
+        if my.rawStringLiterals:
+          add(my.a, 'u')
+        inc(pos, 2)
+        var pos2 = pos
+        var r = parseEscapedUTF16(buf, pos)
+        if r < 0:
+          my.err = errInvalidToken
+          break
+        # Deal with surrogates
+        if (r and 0xfc00) == 0xd800:
+          if buf[pos] != '\\' or buf[pos+1] != 'u':
+            my.err = errInvalidToken
+            break
+          inc(pos, 2)
+          var s = parseEscapedUTF16(buf, pos)
+          if (s and 0xfc00) == 0xdc00 and s > 0:
+            r = 0x10000 + (((r - 0xd800) shl 10) or (s - 0xdc00))
+          else:
+            my.err = errInvalidToken
+            break
+        if my.rawStringLiterals:
+          let length = pos - pos2
+          for i in 1 .. length:
+            if buf[pos2] in {'0'..'9', 'A'..'F', 'a'..'f'}:
+              add(my.a, buf[pos2])
+              inc pos2
+            else:
+              break
+        else:
+          add(my.a, toUTF8(Rune(r)))
+      else:
+        # don't bother with the error
+        add(my.a, buf[pos])
+        inc(pos)
+    of '\c':
+      pos = lexbase.handleCR(my, pos)
+      buf = my.buf
+      add(my.a, '\c')
+    of '\L':
+      pos = lexbase.handleLF(my, pos)
+      buf = my.buf
+      add(my.a, '\L')
+    else:
+      add(my.a, buf[pos])
+      inc(pos)
+  my.bufpos = pos # store back
+
+proc skip(my: var JsonParser) =
+  var pos = my.bufpos
+  var buf = my.buf
+  while true:
+    case buf[pos]
+    of '/':
+      if buf[pos+1] == '/':
+        # skip line comment:
+        inc(pos, 2)
+        while true:
+          case buf[pos]
+          of '\0':
+            break
+          of '\c':
+            pos = lexbase.handleCR(my, pos)
+            buf = my.buf
+            break
+          of '\L':
+            pos = lexbase.handleLF(my, pos)
+            buf = my.buf
+            break
+          else:
+            inc(pos)
+      elif buf[pos+1] == '*':
+        # skip long comment:
+        inc(pos, 2)
+        while true:
+          case buf[pos]
+          of '\0':
+            my.err = errEOC_Expected
+            break
+          of '\c':
+            pos = lexbase.handleCR(my, pos)
+            buf = my.buf
+          of '\L':
+            pos = lexbase.handleLF(my, pos)
+            buf = my.buf
+          of '*':
+            inc(pos)
+            if buf[pos] == '/':
+              inc(pos)
+              break
+          else:
+            inc(pos)
+      else:
+        break
+    of ' ', '\t':
+      inc(pos)
+    of '\c':
+      pos = lexbase.handleCR(my, pos)
+      buf = my.buf
+    of '\L':
+      pos = lexbase.handleLF(my, pos)
+      buf = my.buf
+    else:
+      break
+  my.bufpos = pos
+
+proc parseNumber(my: var JsonParser) =
+  var pos = my.bufpos
+  var buf = my.buf
+  if buf[pos] == '-':
+    add(my.a, '-')
+    inc(pos)
+  if buf[pos] == '.':
+    add(my.a, "0.")
+    inc(pos)
+  else:
+    while buf[pos] in Digits:
+      add(my.a, buf[pos])
+      inc(pos)
+    if buf[pos] == '.':
+      add(my.a, '.')
+      inc(pos)
+  # digits after the dot:
+  while buf[pos] in Digits:
+    add(my.a, buf[pos])
+    inc(pos)
+  if buf[pos] in {'E', 'e'}:
+    add(my.a, buf[pos])
+    inc(pos)
+    if buf[pos] in {'+', '-'}:
+      add(my.a, buf[pos])
+      inc(pos)
+    while buf[pos] in Digits:
+      add(my.a, buf[pos])
+      inc(pos)
+  my.bufpos = pos
+
+proc parseName(my: var JsonParser) =
+  var pos = my.bufpos
+  var buf = my.buf
+  if buf[pos] in IdentStartChars:
+    while buf[pos] in IdentChars:
+      add(my.a, buf[pos])
+      inc(pos)
+  my.bufpos = pos
+
+proc getTok*(my: var JsonParser): TokKind =
+  setLen(my.a, 0)
+  skip(my) # skip whitespace, comments
+  case my.buf[my.bufpos]
+  of '-', '.', '0'..'9':
+    parseNumber(my)
+    if {'.', 'e', 'E'} in my.a:
+      result = tkFloat
+    else:
+      result = tkInt
+  of '"':
+    result = parseString(my)
+  of '[':
+    inc(my.bufpos)
+    result = tkBracketLe
+  of '{':
+    inc(my.bufpos)
+    result = tkCurlyLe
+  of ']':
+    inc(my.bufpos)
+    result = tkBracketRi
+  of '}':
+    inc(my.bufpos)
+    result = tkCurlyRi
+  of ',':
+    inc(my.bufpos)
+    result = tkComma
+  of ':':
+    inc(my.bufpos)
+    result = tkColon
+  of '\0':
+    result = tkEof
+  of 'a'..'z', 'A'..'Z', '_':
+    parseName(my)
+    case my.a
+    of "null": result = tkNull
+    of "true": result = tkTrue
+    of "false": result = tkFalse
+    else: result = tkError
+  else:
+    inc(my.bufpos)
+    result = tkError
+  my.tok = result
+
+
+proc next*(my: var JsonParser) =
+  ## retrieves the first/next event. This controls the parser.
+  var tk = getTok(my)
+  var i = my.state.len-1
+  # the following code is a state machine. If we had proper coroutines,
+  # the code could be much simpler.
+  case my.state[i]
+  of stateEof:
+    if tk == tkEof:
+      my.kind = jsonEof
+    else:
+      my.kind = jsonError
+      my.err = errEofExpected
+  of stateStart:
+    # tokens allowed?
+    case tk
+    of tkString, tkInt, tkFloat, tkTrue, tkFalse, tkNull:
+      my.state[i] = stateEof # expect EOF next!
+      my.kind = JsonEventKind(ord(tk))
+    of tkBracketLe:
+      my.state.add(stateArray) # we expect any
+      my.kind = jsonArrayStart
+    of tkCurlyLe:
+      my.state.add(stateObject)
+      my.kind = jsonObjectStart
+    of tkEof:
+      my.kind = jsonEof
+    else:
+      my.kind = jsonError
+      my.err = errEofExpected
+  of stateObject:
+    case tk
+    of tkString, tkInt, tkFloat, tkTrue, tkFalse, tkNull:
+      my.state.add(stateExpectColon)
+      my.kind = JsonEventKind(ord(tk))
+    of tkBracketLe:
+      my.state.add(stateExpectColon)
+      my.state.add(stateArray)
+      my.kind = jsonArrayStart
+    of tkCurlyLe:
+      my.state.add(stateExpectColon)
+      my.state.add(stateObject)
+      my.kind = jsonObjectStart
+    of tkCurlyRi:
+      my.kind = jsonObjectEnd
+      discard my.state.pop()
+    else:
+      my.kind = jsonError
+      my.err = errCurlyRiExpected
+  of stateArray:
+    case tk
+    of tkString, tkInt, tkFloat, tkTrue, tkFalse, tkNull:
+      my.state.add(stateExpectArrayComma) # expect value next!
+      my.kind = JsonEventKind(ord(tk))
+    of tkBracketLe:
+      my.state.add(stateExpectArrayComma)
+      my.state.add(stateArray)
+      my.kind = jsonArrayStart
+    of tkCurlyLe:
+      my.state.add(stateExpectArrayComma)
+      my.state.add(stateObject)
+      my.kind = jsonObjectStart
+    of tkBracketRi:
+      my.kind = jsonArrayEnd
+      discard my.state.pop()
+    else:
+      my.kind = jsonError
+      my.err = errBracketRiExpected
+  of stateExpectArrayComma:
+    case tk
+    of tkComma:
+      discard my.state.pop()
+      next(my)
+    of tkBracketRi:
+      my.kind = jsonArrayEnd
+      discard my.state.pop() # pop stateExpectArrayComma
+      discard my.state.pop() # pop stateArray
+    else:
+      my.kind = jsonError
+      my.err = errBracketRiExpected
+  of stateExpectObjectComma:
+    case tk
+    of tkComma:
+      discard my.state.pop()
+      next(my)
+    of tkCurlyRi:
+      my.kind = jsonObjectEnd
+      discard my.state.pop() # pop stateExpectObjectComma
+      discard my.state.pop() # pop stateObject
+    else:
+      my.kind = jsonError
+      my.err = errCurlyRiExpected
+  of stateExpectColon:
+    case tk
+    of tkColon:
+      my.state[i] = stateExpectValue
+      next(my)
+    else:
+      my.kind = jsonError
+      my.err = errColonExpected
+  of stateExpectValue:
+    case tk
+    of tkString, tkInt, tkFloat, tkTrue, tkFalse, tkNull:
+      my.state[i] = stateExpectObjectComma
+      my.kind = JsonEventKind(ord(tk))
+    of tkBracketLe:
+      my.state[i] = stateExpectObjectComma
+      my.state.add(stateArray)
+      my.kind = jsonArrayStart
+    of tkCurlyLe:
+      my.state[i] = stateExpectObjectComma
+      my.state.add(stateObject)
+      my.kind = jsonObjectStart
+    else:
+      my.kind = jsonError
+      my.err = errExprExpected
+
+proc raiseParseErr*(p: JsonParser, msg: string) {.noinline, noreturn.} =
+  ## raises an `EJsonParsingError` exception.
+  raise newException(JsonParsingError, errorMsgExpected(p, msg))
+
+proc eat*(p: var JsonParser, tok: TokKind) =
+  if p.tok == tok: discard getTok(p)
+  else: raiseParseErr(p, tokToStr[tok])
diff --git a/lib/pure/parseopt.nim b/lib/pure/parseopt.nim
index ffb69a72b..58e1be0e4 100644
--- a/lib/pure/parseopt.nim
+++ b/lib/pure/parseopt.nim
@@ -54,29 +54,27 @@ type
                               ## or the argument, ``value`` is not "" if
                               ## the option was given a value
 
-{.deprecated: [TCmdLineKind: CmdLineKind, TOptParser: OptParser].}
-
 proc parseWord(s: string, i: int, w: var string,
-               delim: set[char] = {'\x09', ' ', '\0'}): int =
+               delim: set[char] = {'\x09', ' '}): int =
   result = i
-  if s[result] == '\"':
+  if result < s.len and s[result] == '\"':
     inc(result)
-    while not (s[result] in {'\0', '\"'}):
+    while result < s.len and s[result] != '\"':
       add(w, s[result])
       inc(result)
-    if s[result] == '\"': inc(result)
+    if result < s.len and s[result] == '\"': inc(result)
   else:
-    while not (s[result] in delim):
+    while result < s.len and s[result] notin delim:
       add(w, s[result])
       inc(result)
 
 when declared(os.paramCount):
   proc quote(s: string): string =
-    if find(s, {' ', '\t'}) >= 0 and s[0] != '"':
+    if find(s, {' ', '\t'}) >= 0 and s.len > 0 and s[0] != '"':
       if s[0] == '-':
         result = newStringOfCap(s.len)
-        var i = parseWord(s, 0, result, {'\0', ' ', '\x09', ':', '='})
-        if s[i] in {':','='}:
+        var i = parseWord(s, 0, result, {' ', '\x09', ':', '='})
+        if i < s.len and s[i] in {':','='}:
           result.add s[i]
           inc i
         result.add '"'
@@ -144,43 +142,45 @@ proc handleShortOption(p: var OptParser) =
   add(p.key.string, p.cmd[i])
   inc(i)
   p.inShortState = true
-  while p.cmd[i] in {'\x09', ' '}:
+  while i < p.cmd.len and p.cmd[i] in {'\x09', ' '}:
     inc(i)
     p.inShortState = false
-  if p.cmd[i] in {':', '='} or card(p.shortNoVal) > 0 and p.key.string[0] notin p.shortNoVal:
-    if p.cmd[i] in {':', '='}:
+  if i < p.cmd.len and p.cmd[i] in {':', '='} or
+      card(p.shortNoVal) > 0 and p.key.string[0] notin p.shortNoVal:
+    if i < p.cmd.len and p.cmd[i] in {':', '='}:
       inc(i)
     p.inShortState = false
-    while p.cmd[i] in {'\x09', ' '}: inc(i)
+    while i < p.cmd.len and p.cmd[i] in {'\x09', ' '}: inc(i)
     i = parseWord(p.cmd, i, p.val.string)
-  if p.cmd[i] == '\0': p.inShortState = false
+  if i >= p.cmd.len: p.inShortState = false
   p.pos = i
 
 proc next*(p: var OptParser) {.rtl, extern: "npo$1".} =
   ## parses the first or next option; ``p.kind`` describes what token has been
   ## parsed. ``p.key`` and ``p.val`` are set accordingly.
   var i = p.pos
-  while p.cmd[i] in {'\x09', ' '}: inc(i)
+  while i < p.cmd.len and p.cmd[i] in {'\x09', ' '}: inc(i)
   p.pos = i
   setLen(p.key.string, 0)
   setLen(p.val.string, 0)
   if p.inShortState:
     handleShortOption(p)
     return
-  case p.cmd[i]
-  of '\0':
+  if i >= p.cmd.len:
     p.kind = cmdEnd
-  of '-':
+    return
+  if p.cmd[i] == '-':
     inc(i)
-    if p.cmd[i] == '-':
+    if i < p.cmd.len and p.cmd[i] == '-':
       p.kind = cmdLongOption
       inc(i)
-      i = parseWord(p.cmd, i, p.key.string, {'\0', ' ', '\x09', ':', '='})
-      while p.cmd[i] in {'\x09', ' '}: inc(i)
-      if p.cmd[i] in {':', '='} or len(p.longNoVal) > 0 and p.key.string notin p.longNoVal:
-        if p.cmd[i] in {':', '='}:
+      i = parseWord(p.cmd, i, p.key.string, {' ', '\x09', ':', '='})
+      while i < p.cmd.len and p.cmd[i] in {'\x09', ' '}: inc(i)
+      if i < p.cmd.len and p.cmd[i] in {':', '='} or
+          len(p.longNoVal) > 0 and p.key.string notin p.longNoVal:
+        if i < p.cmd.len and p.cmd[i] in {':', '='}:
           inc(i)
-        while p.cmd[i] in {'\x09', ' '}: inc(i)
+        while i < p.cmd.len and p.cmd[i] in {'\x09', ' '}: inc(i)
         p.pos = parseWord(p.cmd, i, p.val.string)
       else:
         p.pos = i
diff --git a/lib/pure/parseopt2.nim b/lib/pure/parseopt2.nim
index 7254337d0..b54a56c0c 100644
--- a/lib/pure/parseopt2.nim
+++ b/lib/pure/parseopt2.nim
@@ -41,8 +41,6 @@ type
                               ## or the argument, ``value`` is not "" if
                               ## the option was given a value
 
-{.deprecated: [TCmdLineKind: CmdLineKind, TOptParser: OptParser].}
-
 proc initOptParser*(cmdline: seq[string]): OptParser {.rtl.} =
   ## Initalizes option parses with cmdline. cmdline should not contain
   ## argument 0 - program name.
@@ -122,8 +120,6 @@ proc cmdLineRest*(p: OptParser): TaintedString {.rtl, extern: "npo2$1", deprecat
 type
   GetoptResult* = tuple[kind: CmdLineKind, key, val: TaintedString]
 
-{.deprecated: [TGetoptResult: GetoptResult].}
-
 iterator getopt*(p: var OptParser): GetoptResult =
   ## This is an convenience iterator for iterating over the given OptParser object.
   ## Example:
diff --git a/lib/pure/parsesql.nim b/lib/pure/parsesql.nim
index fcc757bea..e3bab9a8d 100644
--- a/lib/pure/parsesql.nim
+++ b/lib/pure/parsesql.nim
@@ -45,8 +45,6 @@ type
   SqlLexer* = object of BaseLexer ## the parser object.
     filename: string
 
-{.deprecated: [TToken: Token, TSqlLexer: SqlLexer].}
-
 const
   tokKindToStr: array[TokKind, string] = [
     "invalid", "[EOF]", "identifier", "quoted identifier", "string constant",
diff --git a/lib/pure/parseutils.nim b/lib/pure/parseutils.nim
index 57387e62e..d77790fe0 100644
--- a/lib/pure/parseutils.nim
+++ b/lib/pure/parseutils.nim
@@ -11,7 +11,7 @@
 ##
 ## To unpack raw bytes look at the `streams <streams.html>`_ module.
 
-{.deadCodeElim: on.}
+{.deadCodeElim: on.}  # dce option deprecated
 
 {.push debugger:off .} # the user does not want to trace a part
                        # of the standard library!
@@ -51,9 +51,9 @@ proc parseHex*(s: string, number: var int, start = 0; maxLen = 0): int {.
   ## upper bound. Not more than ```maxLen`` characters are parsed.
   var i = start
   var foundDigit = false
-  if s[i] == '0' and (s[i+1] == 'x' or s[i+1] == 'X'): inc(i, 2)
-  elif s[i] == '#': inc(i)
   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)
+  elif i < last and s[i] == '#': inc(i)
   while i < last:
     case s[i]
     of '_': discard
@@ -76,8 +76,8 @@ proc parseOct*(s: string, number: var int, start = 0): int  {.
   ## the number of the parsed characters or 0 in case of an error.
   var i = start
   var foundDigit = false
-  if s[i] == '0' and (s[i+1] == 'o' or s[i+1] == 'O'): inc(i, 2)
-  while true:
+  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 '_': discard
     of '0'..'7':
@@ -93,8 +93,8 @@ proc parseBin*(s: string, number: var int, start = 0): int  {.
   ## the number of the parsed characters or 0 in case of an error.
   var i = start
   var foundDigit = false
-  if s[i] == '0' and (s[i+1] == 'b' or s[i+1] == 'B'): inc(i, 2)
-  while true:
+  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:
     case s[i]
     of '_': discard
     of '0'..'1':
@@ -108,9 +108,9 @@ proc parseIdent*(s: string, ident: var string, start = 0): int =
   ## parses an identifier and stores it in ``ident``. Returns
   ## the number of the parsed characters or 0 in case of an error.
   var i = start
-  if s[i] in IdentStartChars:
+  if i < s.len and s[i] in IdentStartChars:
     inc(i)
-    while s[i] in IdentChars: inc(i)
+    while i < s.len and s[i] in IdentChars: inc(i)
     ident = substr(s, start, i-1)
     result = i-start
 
@@ -119,11 +119,9 @@ proc parseIdent*(s: string, start = 0): string =
   ## Returns the parsed identifier or an empty string in case of an error.
   result = ""
   var i = start
-
-  if s[i] in IdentStartChars:
+  if i < s.len and s[i] in IdentStartChars:
     inc(i)
-    while s[i] in IdentChars: inc(i)
-
+    while i < s.len and s[i] in IdentChars: inc(i)
     result = substr(s, start, i-1)
 
 proc parseToken*(s: string, token: var string, validChars: set[char],
@@ -134,24 +132,26 @@ proc parseToken*(s: string, token: var string, validChars: set[char],
   ##
   ## **Deprecated since version 0.8.12**: Use ``parseWhile`` instead.
   var i = start
-  while s[i] in validChars: inc(i)
+  while i < s.len and s[i] in validChars: inc(i)
   result = i-start
   token = substr(s, start, i-1)
 
 proc skipWhitespace*(s: string, start = 0): int {.inline.} =
   ## skips the whitespace starting at ``s[start]``. Returns the number of
   ## skipped characters.
-  while s[start+result] in Whitespace: inc(result)
+  while start+result < s.len and s[start+result] in Whitespace: inc(result)
 
 proc skip*(s, token: string, start = 0): int {.inline.} =
   ## skips the `token` starting at ``s[start]``. Returns the length of `token`
   ## or 0 if there was no `token` at ``s[start]``.
-  while result < token.len and s[result+start] == token[result]: inc(result)
+  while start+result < s.len and result < token.len and
+      s[result+start] == token[result]:
+    inc(result)
   if result != token.len: result = 0
 
 proc skipIgnoreCase*(s, token: string, start = 0): int =
   ## same as `skip` but case is ignored for token matching.
-  while result < token.len and
+  while start+result < s.len and result < token.len and
       toLower(s[result+start]) == toLower(token[result]): inc(result)
   if result != token.len: result = 0
 
@@ -159,18 +159,18 @@ proc skipUntil*(s: string, until: set[char], start = 0): int {.inline.} =
   ## Skips all characters until one char from the set `until` is found
   ## or the end is reached.
   ## Returns number of characters skipped.
-  while s[result+start] notin until and s[result+start] != '\0': inc(result)
+  while start+result < s.len and s[result+start] notin until: inc(result)
 
 proc skipUntil*(s: string, until: char, start = 0): int {.inline.} =
   ## Skips all characters until the char `until` is found
   ## or the end is reached.
   ## Returns number of characters skipped.
-  while s[result+start] != until and s[result+start] != '\0': inc(result)
+  while start+result < s.len and s[result+start] != until: inc(result)
 
 proc skipWhile*(s: string, toSkip: set[char], start = 0): int {.inline.} =
   ## Skips all characters while one char from the set `token` is found.
   ## Returns number of characters skipped.
-  while s[result+start] in toSkip and s[result+start] != '\0': inc(result)
+  while start+result < s.len and s[result+start] in toSkip: inc(result)
 
 proc parseUntil*(s: string, token: var string, until: set[char],
                  start = 0): int {.inline.} =
@@ -214,7 +214,7 @@ proc parseWhile*(s: string, token: var string, validChars: set[char],
   ## the number of the parsed characters or 0 in case of an error. A token
   ## consists of the characters in `validChars`.
   var i = start
-  while s[i] in validChars: inc(i)
+  while i < s.len and s[i] in validChars: inc(i)
   result = i-start
   token = substr(s, start, i-1)
 
@@ -231,16 +231,17 @@ proc rawParseInt(s: string, b: var BiggestInt, start = 0): int =
   var
     sign: BiggestInt = -1
     i = start
-  if s[i] == '+': inc(i)
-  elif s[i] == '-':
-    inc(i)
-    sign = 1
-  if s[i] in {'0'..'9'}:
+  if i < s.len:
+    if s[i] == '+': inc(i)
+    elif s[i] == '-':
+      inc(i)
+      sign = 1
+  if i < s.len and s[i] in {'0'..'9'}:
     b = 0
-    while s[i] in {'0'..'9'}:
+    while i < s.len and s[i] in {'0'..'9'}:
       b = b * 10 - (ord(s[i]) - ord('0'))
       inc(i)
-      while s[i] == '_': inc(i) # underscores are allowed and ignored
+      while i < s.len and s[i] == '_': inc(i) # underscores are allowed and ignored
     b = b * sign
     result = i - start
 {.pop.} # overflowChecks
@@ -281,17 +282,17 @@ proc parseSaturatedNatural*(s: string, b: var int, start = 0): int =
   ##   discard parseSaturatedNatural("848", res)
   ##   doAssert res == 848
   var i = start
-  if s[i] == '+': inc(i)
-  if s[i] in {'0'..'9'}:
+  if i < s.len and s[i] == '+': inc(i)
+  if i < s.len and s[i] in {'0'..'9'}:
     b = 0
-    while s[i] in {'0'..'9'}:
+    while i < s.len and s[i] in {'0'..'9'}:
       let c = ord(s[i]) - ord('0')
       if b <= (high(int) - c) div 10:
         b = b * 10 + c
       else:
         b = high(int)
       inc(i)
-      while s[i] == '_': inc(i) # underscores are allowed and ignored
+      while i < s.len and s[i] == '_': inc(i) # underscores are allowed and ignored
     result = i - start
 
 # overflowChecks doesn't work with BiggestUInt
@@ -300,16 +301,16 @@ proc rawParseUInt(s: string, b: var BiggestUInt, start = 0): int =
     res = 0.BiggestUInt
     prev = 0.BiggestUInt
     i = start
-  if s[i] == '+': inc(i) # Allow
-  if s[i] in {'0'..'9'}:
+  if i < s.len and s[i] == '+': inc(i) # Allow
+  if i < s.len and s[i] in {'0'..'9'}:
     b = 0
-    while s[i] in {'0'..'9'}:
+    while i < s.len and s[i] in {'0'..'9'}:
       prev = res
       res = res * 10 + (ord(s[i]) - ord('0')).BiggestUInt
       if prev > res:
         return 0 # overflowChecks emulation
       inc(i)
-      while s[i] == '_': inc(i) # underscores are allowed and ignored
+      while i < s.len and s[i] == '_': inc(i) # underscores are allowed and ignored
     b = res
     result = i - start
 
@@ -364,8 +365,6 @@ type
     ikVar,                   ## ``var`` part of the interpolated string
     ikExpr                   ## ``expr`` part of the interpolated string
 
-{.deprecated: [TInterpolatedKind: InterpolatedKind].}
-
 iterator interpolatedFragments*(s: string): tuple[kind: InterpolatedKind,
   value: string] =
   ## Tokenizes the string `s` into substrings for interpolation purposes.
@@ -389,31 +388,31 @@ iterator interpolatedFragments*(s: string): tuple[kind: InterpolatedKind,
   var kind: InterpolatedKind
   while true:
     var j = i
-    if s[j] == '$':
-      if s[j+1] == '{':
+    if j < s.len and s[j] == '$':
+      if j+1 < s.len and s[j+1] == '{':
         inc j, 2
         var nesting = 0
-        while true:
-          case s[j]
-          of '{': inc nesting
-          of '}':
-            if nesting == 0:
-              inc j
-              break
-            dec nesting
-          of '\0':
-            raise newException(ValueError,
-              "Expected closing '}': " & substr(s, i, s.high))
-          else: discard
-          inc j
+        block curlies:
+          while j < s.len:
+            case s[j]
+            of '{': inc nesting
+            of '}':
+              if nesting == 0:
+                inc j
+                break curlies
+              dec nesting
+            else: discard
+            inc j
+          raise newException(ValueError,
+            "Expected closing '}': " & substr(s, i, s.high))
         inc i, 2 # skip ${
         kind = ikExpr
-      elif s[j+1] in IdentStartChars:
+      elif j+1 < s.len and s[j+1] in IdentStartChars:
         inc j, 2
-        while s[j] in IdentChars: inc(j)
+        while j < s.len and s[j] in IdentChars: inc(j)
         inc i # skip $
         kind = ikVar
-      elif s[j+1] == '$':
+      elif j+1 < s.len and s[j+1] == '$':
         inc j, 2
         inc i # skip $
         kind = ikDollar
diff --git a/lib/pure/parsexml.nim b/lib/pure/parsexml.nim
index 978c9c516..e0000aad3 100644
--- a/lib/pure/parsexml.nim
+++ b/lib/pure/parsexml.nim
@@ -98,9 +98,6 @@ type
     filename: string
     options: set[XmlParseOption]
 
-{.deprecated: [TXmlParser: XmlParser, TXmlParseOptions: XmlParseOption,
-    TXmlError: XmlErrorKind, TXmlEventKind: XmlEventKind].}
-
 const
   errorMessages: array[XmlErrorKind, string] = [
     "no error",
diff --git a/lib/pure/pegs.nim b/lib/pure/pegs.nim
index 5ae2d9182..830429842 100644
--- a/lib/pure/pegs.nim
+++ b/lib/pure/pegs.nim
@@ -74,8 +74,8 @@ type
     line: int                     ## line the symbol has been declared/used in
     col: int                      ## column the symbol has been declared/used in
     flags: set[NonTerminalFlag]   ## the nonterminal's flags
-    rule: Node                   ## the rule that the symbol refers to
-  Node {.shallow.} = object
+    rule: Peg                   ## the rule that the symbol refers to
+  Peg* {.shallow.} = object ## type that represents a PEG
     case kind: PegKind
     of pkEmpty..pkWhitespace: nil
     of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle: term: string
@@ -83,13 +83,9 @@ type
     of pkCharChoice, pkGreedyRepSet: charChoice: ref set[char]
     of pkNonTerminal: nt: NonTerminal
     of pkBackRef..pkBackRefIgnoreStyle: index: range[0..MaxSubpatterns]
-    else: sons: seq[Node]
+    else: sons: seq[Peg]
   NonTerminal* = ref NonTerminalObj
 
-  Peg* = Node ## type that represents a PEG
-
-{.deprecated: [TPeg: Peg, TNode: Node].}
-
 proc term*(t: string): Peg {.nosideEffect, rtl, extern: "npegs$1Str".} =
   ## constructs a PEG from a terminal string
   if t.len != 1:
@@ -534,15 +530,15 @@ proc rawMatch*(s: string, p: Peg, start: int, c: var Captures): int {.
   case p.kind
   of pkEmpty: result = 0 # match of length 0
   of pkAny:
-    if s[start] != '\0': result = 1
+    if start < s.len: result = 1
     else: result = -1
   of pkAnyRune:
-    if s[start] != '\0':
+    if start < s.len:
       result = runeLenAt(s, start)
     else:
       result = -1
   of pkLetter:
-    if s[start] != '\0':
+    if start < s.len:
       var a: Rune
       result = start
       fastRuneAt(s, result, a)
@@ -551,7 +547,7 @@ proc rawMatch*(s: string, p: Peg, start: int, c: var Captures): int {.
     else:
       result = -1
   of pkLower:
-    if s[start] != '\0':
+    if start < s.len:
       var a: Rune
       result = start
       fastRuneAt(s, result, a)
@@ -560,7 +556,7 @@ proc rawMatch*(s: string, p: Peg, start: int, c: var Captures): int {.
     else:
       result = -1
   of pkUpper:
-    if s[start] != '\0':
+    if start < s.len:
       var a: Rune
       result = start
       fastRuneAt(s, result, a)
@@ -569,7 +565,7 @@ proc rawMatch*(s: string, p: Peg, start: int, c: var Captures): int {.
     else:
       result = -1
   of pkTitle:
-    if s[start] != '\0':
+    if start < s.len:
       var a: Rune
       result = start
       fastRuneAt(s, result, a)
@@ -578,7 +574,7 @@ proc rawMatch*(s: string, p: Peg, start: int, c: var Captures): int {.
     else:
       result = -1
   of pkWhitespace:
-    if s[start] != '\0':
+    if start < s.len:
       var a: Rune
       result = start
       fastRuneAt(s, result, a)
@@ -589,15 +585,15 @@ proc rawMatch*(s: string, p: Peg, start: int, c: var Captures): int {.
   of pkGreedyAny:
     result = len(s) - start
   of pkNewLine:
-    if s[start] == '\L': result = 1
-    elif s[start] == '\C':
-      if s[start+1] == '\L': result = 2
+    if start < s.len and s[start] == '\L': result = 1
+    elif start < s.len and s[start] == '\C':
+      if start+1 < s.len and s[start+1] == '\L': result = 2
       else: result = 1
     else: result = -1
   of pkTerminal:
     result = len(p.term)
     for i in 0..result-1:
-      if p.term[i] != s[start+i]:
+      if start+i >= s.len or p.term[i] != s[start+i]:
         result = -1
         break
   of pkTerminalIgnoreCase:
@@ -606,6 +602,9 @@ proc rawMatch*(s: string, p: Peg, start: int, c: var Captures): int {.
       a, b: Rune
     result = start
     while i < len(p.term):
+      if result >= s.len:
+        result = -1
+        break
       fastRuneAt(p.term, i, a)
       fastRuneAt(s, result, b)
       if toLower(a) != toLower(b):
@@ -618,21 +617,26 @@ proc rawMatch*(s: string, p: Peg, start: int, c: var Captures): int {.
       a, b: Rune
     result = start
     while i < len(p.term):
-      while true:
+      while i < len(p.term):
         fastRuneAt(p.term, i, a)
         if a != Rune('_'): break
-      while true:
+      while result < s.len:
         fastRuneAt(s, result, b)
         if b != Rune('_'): break
-      if toLower(a) != toLower(b):
+      if result >= s.len:
+        if i >= p.term.len: break
+        else:
+          result = -1
+          break
+      elif toLower(a) != toLower(b):
         result = -1
         break
     dec(result, start)
   of pkChar:
-    if p.ch == s[start]: result = 1
+    if start < s.len and p.ch == s[start]: result = 1
     else: result = -1
   of pkCharChoice:
-    if contains(p.charChoice[], s[start]): result = 1
+    if start < s.len and contains(p.charChoice[], s[start]): result = 1
     else: result = -1
   of pkNonTerminal:
     var oldMl = c.ml
@@ -695,10 +699,10 @@ proc rawMatch*(s: string, p: Peg, start: int, c: var Captures): int {.
   of pkGreedyRepChar:
     result = 0
     var ch = p.ch
-    while ch == s[start+result]: inc(result)
+    while start+result < s.len and ch == s[start+result]: inc(result)
   of pkGreedyRepSet:
     result = 0
-    while contains(p.charChoice[], s[start+result]): inc(result)
+    while start+result < s.len and contains(p.charChoice[], s[start+result]): inc(result)
   of pkOption:
     result = max(0, rawMatch(s, p.sons[0], start, c))
   of pkAndPredicate:
diff --git a/lib/pure/random.nim b/lib/pure/random.nim
index 968a326d2..01ea9c845 100644
--- a/lib/pure/random.nim
+++ b/lib/pure/random.nim
@@ -192,7 +192,7 @@ when not defined(nimscript):
     ## Initializes the random number generator with a "random"
     ## number, i.e. a tickcount. Note: Does not work for NimScript.
     let now = times.getTime()
-    randomize(convert(Seconds, Nanoseconds, now.toUnix) + now.nanoseconds)
+    randomize(convert(Seconds, Nanoseconds, now.toUnix) + now.nanosecond)
 
 {.pop.}
 
diff --git a/lib/pure/ropes.nim b/lib/pure/ropes.nim
index 6ddd61afa..9b9cdb52a 100644
--- a/lib/pure/ropes.nim
+++ b/lib/pure/ropes.nim
@@ -19,7 +19,7 @@
 include "system/inclrtl"
 import streams
 
-{.deadCodeElim: on.}
+{.deadCodeElim: on.}  # dce option deprecated
 
 {.push debugger:off .} # the user does not want to trace a part
                        # of the standard library!
@@ -37,8 +37,6 @@ type
     length: int
     data: string # != nil if a leaf
 
-{.deprecated: [PRope: Rope].}
-
 proc isConc(r: Rope): bool {.inline.} = return isNil(r.data)
 
 # Note that the left and right pointers are not needed for leafs.
diff --git a/lib/pure/scgi.nim b/lib/pure/scgi.nim
index 1ff26954e..e36803823 100644
--- a/lib/pure/scgi.nim
+++ b/lib/pure/scgi.nim
@@ -94,9 +94,6 @@ type
     disp: Dispatcher
   AsyncScgiState* = ref AsyncScgiStateObj
 
-{.deprecated: [EScgi: ScgiError, TScgiState: ScgiState,
-   PAsyncScgiState: AsyncScgiState].}
-
 proc recvBuffer(s: var ScgiState, L: int) =
   if L > s.bufLen:
     s.bufLen = L
diff --git a/lib/pure/selectors.nim b/lib/pure/selectors.nim
index dda5658a2..640df8282 100644
--- a/lib/pure/selectors.nim
+++ b/lib/pure/selectors.nim
@@ -271,7 +271,7 @@ else:
       msg.add("Internal Error\n")
     var err = newException(IOSelectorsException, msg)
     raise err
-  
+
   proc setNonBlocking(fd: cint) {.inline.} =
     setBlocking(fd.SocketHandle, false)
 
@@ -316,18 +316,18 @@ else:
     include ioselects/ioselectors_poll
 
 proc register*[T](s: Selector[T], fd: int | SocketHandle,
-                  events: set[Event], data: T) {.deprecated.} =
+                  events: set[Event], data: T) {.deprecated: "use registerHandle instead".} =
   ## **Deprecated since v0.18.0:** Use ``registerHandle`` instead.
   s.registerHandle(fd, events, data)
 
-proc setEvent*(ev: SelectEvent) {.deprecated.} =
+proc setEvent*(ev: SelectEvent) {.deprecated: "use trigger instead".} =
   ## Trigger event ``ev``.
   ##
   ## **Deprecated since v0.18.0:** Use ``trigger`` instead.
   ev.trigger()
 
 proc update*[T](s: Selector[T], fd: int | SocketHandle,
-                events: set[Event]) {.deprecated.} =
+                events: set[Event]) {.deprecated: "use updateHandle instead".} =
   ## Update file/socket descriptor ``fd``, registered in selector
   ## ``s`` with new events set ``event``.
   ##
diff --git a/lib/pure/smtp.nim b/lib/pure/smtp.nim
index 08e6c8112..c2c674b84 100644
--- a/lib/pure/smtp.nim
+++ b/lib/pure/smtp.nim
@@ -51,8 +51,6 @@ type
   Smtp* = SmtpBase[Socket]
   AsyncSmtp* = SmtpBase[AsyncSocket]
 
-{.deprecated: [EInvalidReply: ReplyError, TMessage: Message, TSMTP: Smtp].}
-
 proc debugSend(smtp: Smtp | AsyncSmtp, cmd: string) {.multisync.} =
   if smtp.debug:
     echo("C:" & cmd)
diff --git a/lib/pure/stats.nim b/lib/pure/stats.nim
index 964938133..ce32108c2 100644
--- a/lib/pure/stats.nim
+++ b/lib/pure/stats.nim
@@ -65,8 +65,6 @@ type
     y_stats*: RunningStat   ## stats for second set of data
     s_xy: float             ## accumulated data for combined xy
 
-{.deprecated: [TFloatClass: FloatClass, TRunningStat: RunningStat].}
-
 # ----------- RunningStat --------------------------
 proc clear*(s: var RunningStat) =
   ## reset `s`
diff --git a/lib/pure/streams.nim b/lib/pure/streams.nim
index 8fb14b6f6..68922f730 100644
--- a/lib/pure/streams.nim
+++ b/lib/pure/streams.nim
@@ -57,8 +57,6 @@ type
       tags: [WriteIOEffect], gcsafe.}
     flushImpl*: proc (s: Stream) {.nimcall, tags: [WriteIOEffect], gcsafe.}
 
-{.deprecated: [PStream: Stream, TStream: StreamObj].}
-
 proc flush*(s: Stream) =
   ## flushes the buffers that the stream `s` might use.
   if not isNil(s.flushImpl): s.flushImpl(s)
@@ -76,27 +74,14 @@ proc atEnd*(s: Stream): bool =
   ## been read.
   result = s.atEndImpl(s)
 
-proc atEnd*(s, unused: Stream): bool {.deprecated.} =
-  ## checks if more data can be read from `f`. Returns true if all data has
-  ## been read.
-  result = s.atEndImpl(s)
-
 proc setPosition*(s: Stream, pos: int) =
   ## sets the position `pos` of the stream `s`.
   s.setPositionImpl(s, pos)
 
-proc setPosition*(s, unused: Stream, pos: int) {.deprecated.} =
-  ## sets the position `pos` of the stream `s`.
-  s.setPositionImpl(s, pos)
-
 proc getPosition*(s: Stream): int =
   ## retrieves the current position in the stream `s`.
   result = s.getPositionImpl(s)
 
-proc getPosition*(s, unused: Stream): int {.deprecated.} =
-  ## retrieves the current position in the stream `s`.
-  result = s.getPositionImpl(s)
-
 proc readData*(s: Stream, buffer: pointer, bufLen: int): int =
   ## low level proc that reads data into an untyped `buffer` of `bufLen` size.
   result = s.readDataImpl(s, buffer, bufLen)
@@ -114,11 +99,6 @@ proc readAll*(s: Stream): string =
     inc r, bufferSize
     setLen(result, r+bufferSize)
 
-proc readData*(s, unused: Stream, buffer: pointer,
-               bufLen: int): int {.deprecated.} =
-  ## low level proc that reads data into an untyped `buffer` of `bufLen` size.
-  result = s.readDataImpl(s, buffer, bufLen)
-
 proc peekData*(s: Stream, buffer: pointer, bufLen: int): int =
   ## low level proc that reads data into an untyped `buffer` of `bufLen` size
   ## without moving stream position
@@ -153,11 +133,6 @@ proc write*(s: Stream, x: string) =
   else:
     if x.len > 0: writeData(s, unsafeAddr x[0], x.len)
 
-proc writeLn*(s: Stream, args: varargs[string, `$`]) {.deprecated.} =
-  ## **Deprecated since version 0.11.4:** Use **writeLine** instead.
-  for str in args: write(s, str)
-  write(s, "\n")
-
 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.
@@ -348,8 +323,6 @@ when not defined(js):
       data*: string
       pos: int
 
-  {.deprecated: [PStringStream: StringStream, TStringStream: StringStreamObj].}
-
   proc ssAtEnd(s: Stream): bool =
     var s = StringStream(s)
     return s.pos >= s.data.len
@@ -409,7 +382,6 @@ when not defined(js):
     FileStream* = ref FileStreamObj ## a stream that encapsulates a `File`
     FileStreamObj* = object of Stream
       f: File
-  {.deprecated: [PFileStream: FileStream, TFileStream: FileStreamObj].}
 
   proc fsClose(s: Stream) =
     if FileStream(s).f != nil:
@@ -472,9 +444,6 @@ else:
       handle*: FileHandle
       pos: int
 
-  {.deprecated: [PFileHandleStream: FileHandleStream,
-     TFileHandleStream: FileHandleStreamObj].}
-
   proc newEOS(msg: string): ref OSError =
     new(result)
     result.msg = msg
diff --git a/lib/pure/strformat.nim b/lib/pure/strformat.nim
index abdb655d7..a8b128460 100644
--- a/lib/pure/strformat.nim
+++ b/lib/pure/strformat.nim
@@ -278,7 +278,7 @@ template callFormatOption(res, arg, option) {.dirty.} =
 macro `&`*(pattern: string): untyped =
   ## For a specification of the ``&`` macro, see the module level documentation.
   if pattern.kind notin {nnkStrLit..nnkTripleStrLit}:
-    error "& only works with string literals", pattern
+    error "string formatting (fmt(), &) only works with string literals", pattern
   let f = pattern.strVal
   var i = 0
   let res = genSym(nskVar, "fmtRes")
diff --git a/lib/pure/strmisc.nim b/lib/pure/strmisc.nim
index 89ef2fcd2..d1ff920c9 100644
--- a/lib/pure/strmisc.nim
+++ b/lib/pure/strmisc.nim
@@ -12,7 +12,7 @@
 
 import strutils
 
-{.deadCodeElim: on.}
+{.deadCodeElim: on.}  # dce option deprecated
 
 proc expandTabs*(s: string, tabSize: int = 8): string {.noSideEffect,
   procvar.} =
diff --git a/lib/pure/strscans.nim b/lib/pure/strscans.nim
index f8a0c43ee..b0af149b5 100644
--- a/lib/pure/strscans.nim
+++ b/lib/pure/strscans.nim
@@ -87,7 +87,7 @@ which we then use in our scanf pattern to help us in the matching process:
   proc someSep(input: string; start: int; seps: set[char] = {':','-','.'}): int =
     # Note: The parameters and return value must match to what ``scanf`` requires
     result = 0
-    while input[start+result] in seps: inc result
+    while start+result < input.len and input[start+result] in seps: inc result
 
   if scanf(input, "$w$[someSep]$w", key, value):
     ...
@@ -231,7 +231,7 @@ is performed.
     var i = start
     var u = 0
     while true:
-      if s[i] == '\0' or s[i] == unless:
+      if i >= s.len or s[i] == unless:
         return 0
       elif s[i] == until[0]:
         u = 1
@@ -315,6 +315,8 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b
     conds.add resLen.notZero
     conds.add resLen
 
+  template at(s: string; i: int): char = (if i < s.len: s[i] else: '\0')
+
   var i = 0
   var p = 0
   var idx = genSym(nskVar, "idx")
@@ -397,7 +399,7 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b
         var nesting = 0
         let start = p
         while true:
-          case pattern[p]
+          case pattern.at(p)
           of '{': inc nesting
           of '}':
             if nesting == 0: break
@@ -419,7 +421,7 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b
         var nesting = 0
         let start = p
         while true:
-          case pattern[p]
+          case pattern.at(p)
           of '[': inc nesting
           of ']':
             if nesting == 0: break
@@ -451,10 +453,12 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b
 
 template atom*(input: string; idx: int; c: char): bool =
   ## Used in scanp for the matching of atoms (usually chars).
-  input[idx] == c
+  idx < input.len and input[idx] == c
 
 template atom*(input: string; idx: int; s: set[char]): bool =
-  input[idx] in s
+  idx < input.len and input[idx] in s
+
+template hasNxt*(input: string; idx: int): bool = idx < input.len
 
 #template prepare*(input: string): int = 0
 template success*(x: int): bool = x != 0
@@ -462,7 +466,7 @@ template success*(x: int): bool = x != 0
 template nxt*(input: string; idx, step: int = 1) = inc(idx, step)
 
 macro scanp*(input, idx: typed; pattern: varargs[untyped]): bool =
-  ## ``scanp`` is currently undocumented.
+  ## See top level documentation of his module of how ``scanf`` works.
   type StmtTriple = tuple[init, cond, action: NimNode]
 
   template interf(x): untyped = bindSym(x, brForceOpen)
@@ -508,8 +512,8 @@ macro scanp*(input, idx: typed; pattern: varargs[untyped]): bool =
                 !!newCall(interf"nxt", input, idx, resLen))
     of nnkCallKinds:
       # *{'A'..'Z'} !! s.add(!_)
-      template buildWhile(init, cond, action): untyped =
-        while true:
+      template buildWhile(input, idx, init, cond, action): untyped =
+        while hasNxt(input, idx):
           init
           if not cond: break
           action
@@ -528,7 +532,7 @@ macro scanp*(input, idx: typed; pattern: varargs[untyped]): bool =
                   !!newCall(interf"nxt", input, idx, it[2]))
       elif it.kind == nnkPrefix and it[0].eqIdent"*":
         let (init, cond, action) = atm(it[1], input, idx, attached)
-        result = (getAst(buildWhile(init, cond, action)),
+        result = (getAst(buildWhile(input, idx, init, cond, action)),
                   newEmptyNode(), newEmptyNode())
       elif it.kind == nnkPrefix and it[0].eqIdent"+":
         # x+  is the same as  xx*
@@ -621,7 +625,7 @@ macro scanp*(input, idx: typed; pattern: varargs[untyped]): bool =
 
 when isMainModule:
   proc twoDigits(input: string; x: var int; start: int): int =
-    if input[start] == '0' and input[start+1] == '0':
+    if start+1 < input.len and input[start] == '0' and input[start+1] == '0':
       result = 2
       x = 13
     else:
@@ -629,10 +633,10 @@ when isMainModule:
 
   proc someSep(input: string; start: int; seps: set[char] = {';',',','-','.'}): int =
     result = 0
-    while input[start+result] in seps: inc result
+    while start+result < input.len and input[start+result] in seps: inc result
 
   proc demangle(s: string; res: var string; start: int): int =
-    while s[result+start] in {'_', '@'}: inc result
+    while result+start < s.len and s[result+start] in {'_', '@'}: inc result
     res = ""
     while result+start < s.len and s[result+start] > ' ' and s[result+start] != '_':
       res.add s[result+start]
@@ -652,7 +656,7 @@ when isMainModule:
       var info = ""
       if scanp(resp, idx, *`whites`, '#', *`digits`, +`whites`, ?("0x", *`hexdigits`, " in "),
                demangle($input, prc, $index), *`whites`, '(', * ~ ')', ')',
-                *`whites`, "at ", +(~{'\C', '\L', '\0'} -> info.add($_)) ):
+                *`whites`, "at ", +(~{'\C', '\L'} -> info.add($_)) ):
         result.add prc & " " & info
       else:
         break
@@ -713,7 +717,7 @@ when isMainModule:
           "NimMainInner c:/users/anwender/projects/nim/lib/system.nim:2605",
           "NimMain c:/users/anwender/projects/nim/lib/system.nim:2613",
           "main c:/users/anwender/projects/nim/lib/system.nim:2620"]
-  doAssert parseGDB(gdbOut) == result
+  #doAssert parseGDB(gdbOut) == result
 
   # bug #6487
   var count = 0
diff --git a/lib/pure/strtabs.nim b/lib/pure/strtabs.nim
index 75c5e171d..d8a23286a 100644
--- a/lib/pure/strtabs.nim
+++ b/lib/pure/strtabs.nim
@@ -17,9 +17,7 @@ import
 
 when defined(js):
   {.pragma: rtlFunc.}
-  {.pragma: deprecatedGetFunc.}
 else:
-  {.pragma: deprecatedGetFunc, deprecatedGet.}
   {.pragma: rtlFunc, rtl.}
   import os
   include "system/inclrtl"
@@ -29,7 +27,7 @@ type
     modeCaseSensitive,        ## the table is case sensitive
     modeCaseInsensitive,      ## the table is case insensitive
     modeStyleInsensitive      ## the table is style insensitive
-  KeyValuePair = tuple[key, val: string]
+  KeyValuePair = tuple[key, val: string, hasValue: bool]
   KeyValuePairSeq = seq[KeyValuePair]
   StringTableObj* = object of RootObj
     counter: int
@@ -38,9 +36,6 @@ type
 
   StringTableRef* = ref StringTableObj ## use this type to declare string tables
 
-{.deprecated: [TStringTableMode: StringTableMode,
-  TStringTable: StringTableObj, PStringTable: StringTableRef].}
-
 proc len*(t: StringTableRef): int {.rtlFunc, extern: "nst$1".} =
   ## returns the number of keys in `t`.
   result = t.counter
@@ -48,19 +43,19 @@ proc len*(t: StringTableRef): int {.rtlFunc, extern: "nst$1".} =
 iterator pairs*(t: StringTableRef): tuple[key, value: string] =
   ## iterates over every (key, value) pair in the table `t`.
   for h in 0..high(t.data):
-    if not isNil(t.data[h].key):
+    if t.data[h].hasValue:
       yield (t.data[h].key, t.data[h].val)
 
 iterator keys*(t: StringTableRef): string =
   ## iterates over every key in the table `t`.
   for h in 0..high(t.data):
-    if not isNil(t.data[h].key):
+    if t.data[h].hasValue:
       yield t.data[h].key
 
 iterator values*(t: StringTableRef): string =
   ## iterates over every value in the table `t`.
   for h in 0..high(t.data):
-    if not isNil(t.data[h].key):
+    if t.data[h].hasValue:
       yield t.data[h].val
 
 type
@@ -73,10 +68,6 @@ type
     useKey                    ## do not replace ``$key`` if it is not found
                               ## in the table (or in the environment)
 
-{.deprecated: [TFormatFlag: FormatFlag].}
-
-# implementation
-
 const
   growthFactor = 2
   startSize = 64
@@ -102,7 +93,7 @@ proc nextTry(h, maxHash: Hash): Hash {.inline.} =
 
 proc rawGet(t: StringTableRef, key: string): int =
   var h: Hash = myhash(t, key) and high(t.data) # start with real hash value
-  while not isNil(t.data[h].key):
+  while t.data[h].hasValue:
     if myCmp(t, t.data[h].key, key):
       return h
     h = nextTry(h, high(t.data))
@@ -118,17 +109,12 @@ template get(t: StringTableRef, key: string) =
       raise newException(KeyError, "key not found")
 
 proc `[]`*(t: StringTableRef, key: string): var string {.
-           rtlFunc, extern: "nstTake", deprecatedGetFunc.} =
+           rtlFunc, extern: "nstTake".} =
   ## retrieves the location at ``t[key]``. If `key` is not in `t`, the
   ## ``KeyError`` exception is raised. One can check with ``hasKey`` whether
   ## the key exists.
   get(t, key)
 
-proc mget*(t: StringTableRef, key: string): var string {.deprecated.} =
-  ## retrieves the location at ``t[key]``. If `key` is not in `t`, the
-  ## ``KeyError`` exception is raised. Use ```[]``` instead.
-  get(t, key)
-
 proc getOrDefault*(t: StringTableRef; key: string, default: string = ""): string =
   var index = rawGet(t, key)
   if index >= 0: result = t.data[index].val
@@ -144,16 +130,17 @@ proc contains*(t: StringTableRef, key: string): bool =
 
 proc rawInsert(t: StringTableRef, data: var KeyValuePairSeq, key, val: string) =
   var h: Hash = myhash(t, key) and high(data)
-  while not isNil(data[h].key):
+  while data[h].hasValue:
     h = nextTry(h, high(data))
   data[h].key = key
   data[h].val = val
+  data[h].hasValue = true
 
 proc enlarge(t: StringTableRef) =
   var n: KeyValuePairSeq
   newSeq(n, len(t.data) * growthFactor)
   for i in countup(0, high(t.data)):
-    if not isNil(t.data[i].key): rawInsert(t, n, t.data[i].key, t.data[i].val)
+    if t.data[i].hasValue: rawInsert(t, n, t.data[i].key, t.data[i].val)
   swap(t.data, n)
 
 proc `[]=`*(t: StringTableRef, key, val: string) {.rtlFunc, extern: "nstPut".} =
@@ -192,14 +179,14 @@ proc newStringTable*(mode: StringTableMode): StringTableRef {.
   result.counter = 0
   newSeq(result.data, startSize)
 
-proc clear*(s: StringTableRef, mode: StringTableMode) =
+proc clear*(s: StringTableRef, mode: StringTableMode) {.
+  rtlFunc, extern: "nst$1".} =
   ## resets a string table to be empty again.
   s.mode = mode
   s.counter = 0
   s.data.setLen(startSize)
   for i in 0..<s.data.len:
-    if not isNil(s.data[i].key):
-      s.data[i].key = nil
+    s.data[i].hasValue = false
 
 proc newStringTable*(keyValuePairs: varargs[string],
                      mode: StringTableMode): StringTableRef {.
diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim
index d772b066c..ee3072c85 100644
--- a/lib/pure/strutils.nim
+++ b/lib/pure/strutils.nim
@@ -17,7 +17,7 @@ import parseutils
 from math import pow, round, floor, log10
 from algorithm import reverse
 
-{.deadCodeElim: on.}
+{.deadCodeElim: on.}  # dce option deprecated
 
 {.push debugger:off .} # the user does not want to trace a part
                        # of the standard library!
@@ -106,6 +106,12 @@ proc isUpperAscii*(c: char): bool {.noSideEffect, procvar,
   ## This checks ASCII characters only.
   return c in {'A'..'Z'}
 
+template isImpl(call) =
+  if s.len == 0: return false
+  result = true
+  for c in s:
+    if not call(c): return false
+
 proc isAlphaAscii*(s: string): bool {.noSideEffect, procvar,
   rtl, extern: "nsuIsAlphaAsciiStr".} =
   ## Checks whether or not `s` is alphabetical.
@@ -114,12 +120,7 @@ proc isAlphaAscii*(s: string): bool {.noSideEffect, procvar,
   ## Returns true if all characters in `s` are
   ## alphabetic and there is at least one character
   ## in `s`.
-  if s.len() == 0:
-    return false
-
-  result = true
-  for c in s:
-    if not c.isAlphaAscii(): return false
+  isImpl isAlphaAscii
 
 proc isAlphaNumeric*(s: string): bool {.noSideEffect, procvar,
   rtl, extern: "nsuIsAlphaNumericStr".} =
@@ -129,13 +130,7 @@ proc isAlphaNumeric*(s: string): bool {.noSideEffect, procvar,
   ## Returns true if all characters in `s` are
   ## alpanumeric and there is at least one character
   ## in `s`.
-  if s.len() == 0:
-    return false
-
-  result = true
-  for c in s:
-    if not c.isAlphaNumeric():
-      return false
+  isImpl isAlphaNumeric
 
 proc isDigit*(s: string): bool {.noSideEffect, procvar,
   rtl, extern: "nsuIsDigitStr".} =
@@ -145,13 +140,7 @@ proc isDigit*(s: string): bool {.noSideEffect, procvar,
   ## Returns true if all characters in `s` are
   ## numeric and there is at least one character
   ## in `s`.
-  if s.len() == 0:
-    return false
-
-  result = true
-  for c in s:
-    if not c.isDigit():
-      return false
+  isImpl isDigit
 
 proc isSpaceAscii*(s: string): bool {.noSideEffect, procvar,
   rtl, extern: "nsuIsSpaceAsciiStr".} =
@@ -159,13 +148,7 @@ proc isSpaceAscii*(s: string): bool {.noSideEffect, procvar,
   ##
   ## Returns true if all characters in `s` are whitespace
   ## characters and there is at least one character in `s`.
-  if s.len() == 0:
-    return false
-
-  result = true
-  for c in s:
-    if not c.isSpaceAscii():
-      return false
+  isImpl isSpaceAscii
 
 proc isLowerAscii*(s: string): bool {.noSideEffect, procvar,
   rtl, extern: "nsuIsLowerAsciiStr".} =
@@ -174,13 +157,7 @@ proc isLowerAscii*(s: string): bool {.noSideEffect, procvar,
   ## This checks ASCII characters only.
   ## Returns true if all characters in `s` are lower case
   ## and there is at least one character  in `s`.
-  if s.len() == 0:
-    return false
-
-  for c in s:
-    if not c.isLowerAscii():
-      return false
-  true
+  isImpl isLowerAscii
 
 proc isUpperAscii*(s: string): bool {.noSideEffect, procvar,
   rtl, extern: "nsuIsUpperAsciiStr".} =
@@ -189,13 +166,7 @@ proc isUpperAscii*(s: string): bool {.noSideEffect, procvar,
   ## This checks ASCII characters only.
   ## Returns true if all characters in `s` are upper case
   ## and there is at least one character in `s`.
-  if s.len() == 0:
-    return false
-
-  for c in s:
-    if not c.isUpperAscii():
-      return false
-  true
+  isImpl isUpperAscii
 
 proc toLowerAscii*(c: char): char {.noSideEffect, procvar,
   rtl, extern: "nsuToLowerAsciiChar".} =
@@ -209,6 +180,11 @@ proc toLowerAscii*(c: char): char {.noSideEffect, procvar,
   else:
     result = c
 
+template toImpl(call) =
+  result = newString(len(s))
+  for i in 0..len(s) - 1:
+    result[i] = call(s[i])
+
 proc toLowerAscii*(s: string): string {.noSideEffect, procvar,
   rtl, extern: "nsuToLowerAsciiStr".} =
   ## Converts `s` into lower case.
@@ -216,9 +192,7 @@ proc toLowerAscii*(s: string): string {.noSideEffect, procvar,
   ## This works only for the letters ``A-Z``. See `unicode.toLower
   ## <unicode.html#toLower>`_ for a version that works for any Unicode
   ## character.
-  result = newString(len(s))
-  for i in 0..len(s) - 1:
-    result[i] = toLowerAscii(s[i])
+  toImpl toLowerAscii
 
 proc toUpperAscii*(c: char): char {.noSideEffect, procvar,
   rtl, extern: "nsuToUpperAsciiChar".} =
@@ -239,147 +213,15 @@ proc toUpperAscii*(s: string): string {.noSideEffect, procvar,
   ## This works only for the letters ``A-Z``.  See `unicode.toUpper
   ## <unicode.html#toUpper>`_ for a version that works for any Unicode
   ## character.
-  result = newString(len(s))
-  for i in 0..len(s) - 1:
-    result[i] = toUpperAscii(s[i])
+  toImpl toUpperAscii
 
 proc capitalizeAscii*(s: string): string {.noSideEffect, procvar,
   rtl, extern: "nsuCapitalizeAscii".} =
   ## Converts the first character of `s` into upper case.
   ##
   ## This works only for the letters ``A-Z``.
-  result = toUpperAscii(s[0]) & substr(s, 1)
-
-proc isSpace*(c: char): bool {.noSideEffect, procvar,
-  rtl, deprecated, extern: "nsuIsSpaceChar".}=
-  ## Checks whether or not `c` is a whitespace character.
-  ##
-  ## **Deprecated since version 0.15.0**: use ``isSpaceAscii`` instead.
-  isSpaceAscii(c)
-
-proc isLower*(c: char): bool {.noSideEffect, procvar,
-  rtl, deprecated, extern: "nsuIsLowerChar".}=
-  ## Checks whether or not `c` is a lower case character.
-  ##
-  ## This checks ASCII characters only.
-  ##
-  ## **Deprecated since version 0.15.0**: use ``isLowerAscii`` instead.
-  isLowerAscii(c)
-
-proc isUpper*(c: char): bool {.noSideEffect, procvar,
-  rtl, deprecated, extern: "nsuIsUpperChar".}=
-  ## Checks whether or not `c` is an upper case character.
-  ##
-  ## This checks ASCII characters only.
-  ##
-  ## **Deprecated since version 0.15.0**: use ``isUpperAscii`` instead.
-  isUpperAscii(c)
-
-proc isAlpha*(c: char): bool {.noSideEffect, procvar,
-  rtl, deprecated, extern: "nsuIsAlphaChar".}=
-  ## Checks whether or not `c` is alphabetical.
-  ##
-  ## This checks a-z, A-Z ASCII characters only.
-  ##
-  ## **Deprecated since version 0.15.0**: use ``isAlphaAscii`` instead.
-  isAlphaAscii(c)
-
-proc isAlpha*(s: string): bool {.noSideEffect, procvar,
-  rtl, deprecated, extern: "nsuIsAlphaStr".}=
-  ## Checks whether or not `s` is alphabetical.
-  ##
-  ## This checks a-z, A-Z ASCII characters only.
-  ## Returns true if all characters in `s` are
-  ## alphabetic and there is at least one character
-  ## in `s`.
-  ##
-  ## **Deprecated since version 0.15.0**: use ``isAlphaAscii`` instead.
-  isAlphaAscii(s)
-
-proc isSpace*(s: string): bool {.noSideEffect, procvar,
-  rtl, deprecated, extern: "nsuIsSpaceStr".}=
-  ## Checks whether or not `s` is completely whitespace.
-  ##
-  ## Returns true if all characters in `s` are whitespace
-  ## characters and there is at least one character in `s`.
-  ##
-  ## **Deprecated since version 0.15.0**: use ``isSpaceAscii`` instead.
-  isSpaceAscii(s)
-
-proc isLower*(s: string): bool {.noSideEffect, procvar,
-  rtl, deprecated, extern: "nsuIsLowerStr".}=
-  ## Checks whether or not `s` contains all lower case characters.
-  ##
-  ## This checks ASCII characters only.
-  ## Returns true if all characters in `s` are lower case
-  ## and there is at least one character  in `s`.
-  ##
-  ## **Deprecated since version 0.15.0**: use ``isLowerAscii`` instead.
-  isLowerAscii(s)
-
-proc isUpper*(s: string): bool {.noSideEffect, procvar,
-  rtl, deprecated, extern: "nsuIsUpperStr".}=
-  ## Checks whether or not `s` contains all upper case characters.
-  ##
-  ## This checks ASCII characters only.
-  ## Returns true if all characters in `s` are upper case
-  ## and there is at least one character in `s`.
-  ##
-  ## **Deprecated since version 0.15.0**: use ``isUpperAscii`` instead.
-  isUpperAscii(s)
-
-proc toLower*(c: char): char {.noSideEffect, procvar,
-  rtl, deprecated, extern: "nsuToLowerChar".} =
-  ## Converts `c` into lower case.
-  ##
-  ## This works only for the letters ``A-Z``. See `unicode.toLower
-  ## <unicode.html#toLower>`_ for a version that works for any Unicode
-  ## character.
-  ##
-  ## **Deprecated since version 0.15.0**: use ``toLowerAscii`` instead.
-  toLowerAscii(c)
-
-proc toLower*(s: string): string {.noSideEffect, procvar,
-  rtl, deprecated, extern: "nsuToLowerStr".} =
-  ## Converts `s` into lower case.
-  ##
-  ## This works only for the letters ``A-Z``. See `unicode.toLower
-  ## <unicode.html#toLower>`_ for a version that works for any Unicode
-  ## character.
-  ##
-  ## **Deprecated since version 0.15.0**: use ``toLowerAscii`` instead.
-  toLowerAscii(s)
-
-proc toUpper*(c: char): char {.noSideEffect, procvar,
-  rtl, deprecated, extern: "nsuToUpperChar".} =
-  ## Converts `c` into upper case.
-  ##
-  ## This works only for the letters ``A-Z``.  See `unicode.toUpper
-  ## <unicode.html#toUpper>`_ for a version that works for any Unicode
-  ## character.
-  ##
-  ## **Deprecated since version 0.15.0**: use ``toUpperAscii`` instead.
-  toUpperAscii(c)
-
-proc toUpper*(s: string): string {.noSideEffect, procvar,
-  rtl, deprecated, extern: "nsuToUpperStr".} =
-  ## Converts `s` into upper case.
-  ##
-  ## This works only for the letters ``A-Z``.  See `unicode.toUpper
-  ## <unicode.html#toUpper>`_ for a version that works for any Unicode
-  ## character.
-  ##
-  ## **Deprecated since version 0.15.0**: use ``toUpperAscii`` instead.
-  toUpperAscii(s)
-
-proc capitalize*(s: string): string {.noSideEffect, procvar,
-  rtl, deprecated, extern: "nsuCapitalize".} =
-  ## Converts the first character of `s` into upper case.
-  ##
-  ## This works only for the letters ``A-Z``.
-  ##
-  ## **Deprecated since version 0.15.0**: use ``capitalizeAscii`` instead.
-  capitalizeAscii(s)
+  if s.len == 0: result = ""
+  else: result = toUpperAscii(s[0]) & substr(s, 1)
 
 proc normalize*(s: string): string {.noSideEffect, procvar,
   rtl, extern: "nsuNormalize".} =
@@ -419,9 +261,9 @@ proc cmpIgnoreCase*(a, b: string): int {.noSideEffect,
 proc cmpIgnoreStyle*(a, b: string): int {.noSideEffect,
   rtl, extern: "nsuCmpIgnoreStyle", procvar.} =
   ## Semantically the same as ``cmp(normalize(a), normalize(b))``. It
-  ## is just optimized to not allocate temporary strings.  This should
+  ## is just optimized to not allocate temporary strings. This should
   ## NOT be used to compare Nim identifier names. use `macros.eqIdent`
-  ## for that.  Returns:
+  ## for that. Returns:
   ##
   ## | 0 iff a == b
   ## | < 0 iff a < b
@@ -429,14 +271,22 @@ proc cmpIgnoreStyle*(a, b: string): int {.noSideEffect,
   var i = 0
   var j = 0
   while true:
-    while a[i] == '_': inc(i)
-    while b[j] == '_': inc(j) # BUGFIX: typo
-    var aa = toLowerAscii(a[i])
-    var bb = toLowerAscii(b[j])
+    while i < a.len and a[i] == '_': inc i
+    while j < b.len and b[j] == '_': inc j
+    var aa = if i < a.len: toLowerAscii(a[i]) else: '\0'
+    var bb = if j < b.len: toLowerAscii(b[j]) else: '\0'
     result = ord(aa) - ord(bb)
-    if result != 0 or aa == '\0': break
-    inc(i)
-    inc(j)
+    if result != 0: return result
+    # the characters are identical:
+    if i >= a.len:
+      # both cursors at the end:
+      if j >= b.len: return 0
+      # not yet at the end of 'b':
+      return -1
+    elif j >= b.len:
+      return 1
+    inc i
+    inc j
 
 proc strip*(s: string, leading = true, trailing = true,
             chars: set[char] = Whitespace): string
@@ -451,7 +301,7 @@ proc strip*(s: string, leading = true, trailing = true,
     first = 0
     last = len(s)-1
   if leading:
-    while s[first] in chars: inc(first)
+    while first <= last and s[first] in chars: inc(first)
   if trailing:
     while last >= 0 and s[last] in chars: dec(last)
   result = substr(s, first, last)
@@ -467,7 +317,9 @@ proc toOctal*(c: char): string {.noSideEffect, rtl, extern: "nsuToOctal".} =
     result[i] = chr(val mod 8 + ord('0'))
     val = val div 8
 
-proc isNilOrEmpty*(s: string): bool {.noSideEffect, procvar, rtl, extern: "nsuIsNilOrEmpty".} =
+proc isNilOrEmpty*(s: string): bool {.noSideEffect, procvar, rtl,
+                                      extern: "nsuIsNilOrEmpty",
+                                      deprecated: "use 'x.len == 0' instead".} =
   ## Checks if `s` is nil or empty.
   result = len(s) == 0
 
@@ -486,7 +338,6 @@ proc substrEq(s: string, pos: int, substr: string): bool =
   var length = substr.len
   while i < length and s[pos+i] == substr[i]:
     inc i
-
   return i == length
 
 # --------- Private templates for different split separators -----------
@@ -520,7 +371,7 @@ template oldSplit(s, seps, maxsplit) =
   var splits = maxsplit
   assert(not ('\0' in seps))
   while last < len(s):
-    while s[last] in seps: inc(last)
+    while last < len(s) and s[last] in seps: inc(last)
     var first = last
     while last < len(s) and s[last] notin seps: inc(last)
     if first <= last-1:
@@ -571,10 +422,7 @@ iterator split*(s: string, seps: set[char] = Whitespace,
   ##   "08"
   ##   "08.398990"
   ##
-  when defined(nimOldSplit):
-    oldSplit(s, seps, maxsplit)
-  else:
-    splitCommon(s, seps, maxsplit, 1)
+  splitCommon(s, seps, maxsplit, 1)
 
 iterator splitWhitespace*(s: string, maxsplit: int = -1): string =
   ## Splits the string ``s`` at whitespace stripping leading and trailing
@@ -660,7 +508,6 @@ iterator split*(s: string, sep: string, maxsplit: int = -1): string =
   ##   "is"
   ##   "corrupted"
   ##
-
   splitCommon(s, sep, maxsplit, sep.len)
 
 template rsplitCommon(s, sep, maxsplit, sepLen) =
@@ -670,29 +517,21 @@ template rsplitCommon(s, sep, maxsplit, sepLen) =
     first = last
     splits = maxsplit
     startPos = 0
-
   # go to -1 in order to get separators at the beginning
   while first >= -1:
     while first >= 0 and not stringHasSep(s, first, sep):
       dec(first)
-
     if splits == 0:
       # No more splits means set first to the beginning
       first = -1
-
     if first == -1:
       startPos = 0
     else:
       startPos = first + sepLen
-
     yield substr(s, startPos, last)
-
-    if splits == 0:
-      break
-
+    if splits == 0: break
     dec(splits)
     dec(first)
-
     last = first
 
 iterator rsplit*(s: string, seps: set[char] = Whitespace,
@@ -712,7 +551,6 @@ iterator rsplit*(s: string, seps: set[char] = Whitespace,
   ##   "foo"
   ##
   ## Substrings are separated from the right by the set of chars `seps`
-
   rsplitCommon(s, seps, maxsplit, 1)
 
 iterator rsplit*(s: string, sep: char,
@@ -779,14 +617,14 @@ iterator splitLines*(s: string): string =
   var first = 0
   var last = 0
   while true:
-    while s[last] notin {'\0', '\c', '\l'}: inc(last)
+    while last < s.len and s[last] notin {'\c', '\l'}: inc(last)
     yield substr(s, first, last-1)
     # skip newlines:
+    if last >= s.len: break
     if s[last] == '\l': inc(last)
     elif s[last] == '\c':
       inc(last)
-      if s[last] == '\l': inc(last)
-    else: break # was '\0'
+      if last < s.len and s[last] == '\l': inc(last)
     first = last
 
 proc splitLines*(s: string): seq[string] {.noSideEffect,
@@ -811,7 +649,7 @@ proc countLines*(s: string): int {.noSideEffect,
   while i < s.len:
     case s[i]
     of '\c':
-      if s[i+1] == '\l': inc i
+      if i+1 < s.len and s[i+1] == '\l': inc i
       inc result
     of '\l': inc result
     else: discard
@@ -1025,9 +863,9 @@ proc parseHexInt*(s: string): int {.noSideEffect, procvar,
   ## of the following optional prefixes: ``0x``, ``0X``, ``#``.  Underscores
   ## within `s` are ignored.
   var i = 0
-  if s[i] == '0' and (s[i+1] == 'x' or s[i+1] == 'X'): inc(i, 2)
-  elif s[i] == '#': inc(i)
-  while true:
+  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':
@@ -1039,7 +877,6 @@ proc parseHexInt*(s: string): int {.noSideEffect, procvar,
     of 'A'..'F':
       result = result shl 4 or (ord(s[i]) - ord('A') + 10)
       inc(i)
-    of '\0': break
     else: raise newException(ValueError, "invalid integer: " & s)
 
 proc generateHexCharToValueMap(): string =
@@ -1148,14 +985,6 @@ template spaces*(n: Natural): string = repeat(' ', n)
   ##   echo text1 & spaces(max(0, width - text1.len)) & "|"
   ##   echo text2 & spaces(max(0, width - text2.len)) & "|"
 
-proc repeatChar*(count: Natural, c: char = ' '): string {.deprecated.} =
-  ## deprecated: use repeat() or spaces()
-  repeat(c, count)
-
-proc repeatStr*(count: Natural, s: string): string {.deprecated.} =
-  ## deprecated: use repeat(string, count) or string.repeat(count)
-  repeat(s, count)
-
 proc align*(s: string, count: Natural, padding = ' '): string {.
   noSideEffect, rtl, extern: "nsuAlignString".} =
   ## Aligns a string `s` with `padding`, so that it is of length `count`.
@@ -1226,7 +1055,7 @@ iterator tokenize*(s: string, seps: set[char] = Whitespace): tuple[
   var i = 0
   while true:
     var j = i
-    var isSep = s[j] in seps
+    var isSep = j < s.len and s[j] in seps
     while j < s.len and (s[j] in seps) == isSep: inc(j)
     if j > i:
       yield (substr(s, i, j-1), isSep)
@@ -1297,7 +1126,7 @@ proc unindent*(s: string, count: Natural, padding: string = " "): string
     var indentCount = 0
     for j in 0..<count.int:
       indentCount.inc
-      if line[j .. j + padding.len-1] != padding:
+      if j + padding.len-1 >= line.len or line[j .. j + padding.len-1] != padding:
         indentCount = j
         break
     result.add(line[indentCount*padding.len .. ^1])
@@ -1325,13 +1154,13 @@ proc startsWith*(s, prefix: string): bool {.noSideEffect,
   ## If ``prefix == ""`` true is returned.
   var i = 0
   while true:
-    if prefix[i] == '\0': return true
-    if s[i] != prefix[i]: return false
+    if i >= prefix.len: return true
+    if i >= s.len or s[i] != prefix[i]: return false
     inc(i)
 
 proc startsWith*(s: string, prefix: char): bool {.noSideEffect, inline.} =
   ## Returns true iff ``s`` starts with ``prefix``.
-  result = s[0] == prefix
+  result = s.len > 0 and s[0] == prefix
 
 proc endsWith*(s, suffix: string): bool {.noSideEffect,
   rtl, extern: "nsuEndsWith".} =
@@ -1343,11 +1172,11 @@ proc endsWith*(s, suffix: string): bool {.noSideEffect,
   while i+j <% s.len:
     if s[i+j] != suffix[i]: return false
     inc(i)
-  if suffix[i] == '\0': return true
+  if i >= suffix.len: return true
 
 proc endsWith*(s: string, suffix: char): bool {.noSideEffect, inline.} =
   ## Returns true iff ``s`` ends with ``suffix``.
-  result = s[s.high] == suffix
+  result = s.len > 0 and s[s.high] == suffix
 
 proc continuesWith*(s, substr: string, start: Natural): bool {.noSideEffect,
   rtl, extern: "nsuContinuesWith".} =
@@ -1356,8 +1185,8 @@ proc continuesWith*(s, substr: string, start: Natural): bool {.noSideEffect,
   ## If ``substr == ""`` true is returned.
   var i = 0
   while true:
-    if substr[i] == '\0': return true
-    if s[i+start] != substr[i]: return false
+    if i >= substr.len: return true
+    if i+start >= s.len or s[i+start] != substr[i]: return false
     inc(i)
 
 proc addSep*(dest: var string, sep = ", ", startLen: Natural = 0)
@@ -1433,21 +1262,20 @@ proc initSkipTable*(a: var SkipTable, sub: string)
   {.noSideEffect, rtl, extern: "nsuInitSkipTable".} =
   ## Preprocess table `a` for `sub`.
   let m = len(sub)
-  let m1 = m + 1
   var i = 0
   while i <= 0xff-7:
-    a[chr(i + 0)] = m1
-    a[chr(i + 1)] = m1
-    a[chr(i + 2)] = m1
-    a[chr(i + 3)] = m1
-    a[chr(i + 4)] = m1
-    a[chr(i + 5)] = m1
-    a[chr(i + 6)] = m1
-    a[chr(i + 7)] = m1
+    a[chr(i + 0)] = m
+    a[chr(i + 1)] = m
+    a[chr(i + 2)] = m
+    a[chr(i + 3)] = m
+    a[chr(i + 4)] = m
+    a[chr(i + 5)] = m
+    a[chr(i + 6)] = m
+    a[chr(i + 7)] = m
     i += 8
 
-  for i in 0..m-1:
-    a[sub[i]] = m-i
+  for i in 0 ..< m - 1:
+    a[sub[i]] = m - 1 - i
 
 proc find*(a: SkipTable, s, sub: string, start: Natural = 0, last: Natural = 0): int
   {.noSideEffect, rtl, extern: "nsuFindStrA".} =
@@ -1455,18 +1283,29 @@ proc find*(a: SkipTable, s, sub: string, start: Natural = 0, last: Natural = 0):
   ## If `last` is unspecified, it defaults to `s.high`.
   ##
   ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned.
+
   let
     last = if last==0: s.high else: last
-    m = len(sub)
-    n = last + 1
-  # search:
-  var j = start
-  while j <= n - m:
-    block match:
-      for k in 0..m-1:
-        if sub[k] != s[k+j]: break match
-      return j
-    inc(j, a[s[j+m]])
+    sLen = last - start + 1
+    subLast = sub.len - 1
+
+  if subLast == -1:
+    # this was an empty needle string,
+    # we count this as match in the first possible position:
+    return start
+
+  # This is an implementation of the Boyer-Moore Horspool algorithms
+  # https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore%E2%80%93Horspool_algorithm
+  var skip = start
+
+  while last - skip >= subLast:
+    var i = subLast
+    while s[skip + i] == sub[i]:
+      if i == 0:
+        return skip
+      dec i
+    inc skip, a[s[skip + subLast]]
+
   return -1
 
 when not (defined(js) or defined(nimdoc) or defined(nimscript)):
@@ -1502,12 +1341,8 @@ proc find*(s, sub: string, start: Natural = 0, last: Natural = 0): int {.noSideE
   ## If `last` is unspecified, it defaults to `s.high`.
   ##
   ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned.
-  if sub.len > s.len:
-    return -1
-
-  if sub.len == 1:
-    return find(s, sub[0], start, last)
-
+  if sub.len > s.len: return -1
+  if sub.len == 1: return find(s, sub[0], start, last)
   var a {.noinit.}: SkipTable
   initSkipTable(a, sub)
   result = find(a, s, sub, start, last)
@@ -1564,18 +1399,14 @@ proc center*(s: string, width: int, fillChar: char = ' '): string {.
   ##
   ## The original string is returned if `width` is less than or equal
   ## to `s.len`.
-  if width <= s.len:
-    return s
-
+  if width <= s.len: return s
   result = newString(width)
-
   # Left padding will be one fillChar
   # smaller if there are an odd number
   # of characters
   let
     charsLeft = (width - s.len)
     leftPadding = charsLeft div 2
-
   for i in 0 ..< width:
     if i >= leftPadding and i < leftPadding + s.len:
       # we are where the string should be located
@@ -1593,27 +1424,22 @@ proc count*(s: string, sub: string, overlapping: bool = false): int {.
   var i = 0
   while true:
     i = s.find(sub, i)
-    if i < 0:
-      break
-    if overlapping:
-      inc i
-    else:
-      i += sub.len
+    if i < 0: break
+    if overlapping: inc i
+    else: i += sub.len
     inc result
 
 proc count*(s: string, sub: char): int {.noSideEffect,
   rtl, extern: "nsuCountChar".} =
   ## Count the occurrences of the character `sub` in the string `s`.
   for c in s:
-    if c == sub:
-      inc result
+    if c == sub: inc result
 
 proc count*(s: string, subs: set[char]): int {.noSideEffect,
   rtl, extern: "nsuCountCharSet".} =
   ## Count the occurrences of the group of character `subs` in the string `s`.
   for c in s:
-    if c in subs:
-      inc result
+    if c in subs: inc result
 
 proc quoteIfContainsWhite*(s: string): string {.deprecated.} =
   ## Returns ``'"' & s & '"'`` if `s` contains a space and does not
@@ -1621,10 +1447,8 @@ proc quoteIfContainsWhite*(s: string): string {.deprecated.} =
   ##
   ## **DEPRECATED** as it was confused for shell quoting function.  For this
   ## application use `osproc.quoteShell <osproc.html#quoteShell>`_.
-  if find(s, {' ', '\t'}) >= 0 and s[0] != '"':
-    result = '"' & s & '"'
-  else:
-    result = s
+  if find(s, {' ', '\t'}) >= 0 and s[0] != '"': result = '"' & s & '"'
+  else: result = s
 
 proc contains*(s: string, c: char): bool {.noSideEffect.} =
   ## Same as ``find(s, c) >= 0``.
@@ -1641,19 +1465,41 @@ proc contains*(s: string, chars: set[char]): bool {.noSideEffect.} =
 proc replace*(s, sub: string, by = ""): string {.noSideEffect,
   rtl, extern: "nsuReplaceStr".} =
   ## Replaces `sub` in `s` by the string `by`.
-  var a {.noinit.}: SkipTable
   result = ""
-  initSkipTable(a, sub)
-  let last = s.high
-  var i = 0
-  while true:
-    var j = find(a, s, sub, i, last)
-    if j < 0: break
-    add result, substr(s, i, j - 1)
+  let subLen = sub.len
+  if subLen == 0:
+    for c in s:
+      add result, by
+      add result, c
     add result, by
-    i = j + len(sub)
-  # copy the rest:
-  add result, substr(s, i)
+    return
+  elif subLen == 1:
+    # when the pattern is a single char, we use a faster
+    # char-based search that doesn't need a skip table:
+    var c = sub[0]
+    let last = s.high
+    var i = 0
+    while true:
+      let j = find(s, c, i, last)
+      if j < 0: break
+      add result, substr(s, i, j - 1)
+      add result, by
+      i = j + subLen
+    # copy the rest:
+    add result, substr(s, i)
+  else:
+    var a {.noinit.}: SkipTable
+    initSkipTable(a, sub)
+    let last = s.high
+    var i = 0
+    while true:
+      let j = find(a, s, sub, i, last)
+      if j < 0: break
+      add result, substr(s, i, j - 1)
+      add result, by
+      i = j + subLen
+    # copy the rest:
+    add result, substr(s, i)
 
 proc replace*(s: string, sub, by: char): string {.noSideEffect,
   rtl, extern: "nsuReplaceChar".} =
@@ -1674,12 +1520,14 @@ proc replaceWord*(s, sub: string, by = ""): string {.noSideEffect,
   ## Each occurrence of `sub` has to be surrounded by word boundaries
   ## (comparable to ``\\w`` in regular expressions), otherwise it is not
   ## replaced.
+  if sub.len == 0: return s
   const wordChars = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\128'..'\255'}
   var a {.noinit.}: SkipTable
   result = ""
   initSkipTable(a, sub)
   var i = 0
   let last = s.high
+  let sublen = max(sub.len, 1)
   while true:
     var j = find(a, s, sub, i, last)
     if j < 0: break
@@ -1688,7 +1536,7 @@ proc replaceWord*(s, sub: string, by = ""): string {.noSideEffect,
         (j+sub.len >= s.len or s[j+sub.len] notin wordChars):
       add result, substr(s, i, j - 1)
       add result, by
-      i = j + len(sub)
+      i = j + sublen
     else:
       add result, substr(s, i, j)
       i = j + 1
@@ -1699,9 +1547,8 @@ proc multiReplace*(s: string, replacements: varargs[(string, string)]): string {
   ## Same as replace, but specialized for doing multiple replacements in a single
   ## pass through the input string.
   ##
-  ## Calling replace multiple times after each other is inefficient and result in too many allocations
-  ## follwed by immediate deallocations as portions of the string gets replaced.
-  ## multiReplace performs all replacements in a single pass.
+  ## multiReplace performs all replacements in a single pass, this means it can be used
+  ## to swap the occurences of "a" and "b", for instance.
   ##
   ## If the resulting string is not longer than the original input string, only a single
   ## memory allocation is required.
@@ -1748,14 +1595,13 @@ proc parseOctInt*(s: string): int {.noSideEffect,
   ## of the following optional prefixes: ``0o``, ``0O``.  Underscores within
   ## `s` are ignored.
   var i = 0
-  if s[i] == '0' and (s[i+1] == 'o' or s[i+1] == 'O'): inc(i, 2)
-  while true:
+  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)
-    of '\0': break
     else: raise newException(ValueError, "invalid integer: " & s)
 
 proc toOct*(x: BiggestInt, len: Positive): string {.noSideEffect,
@@ -1813,20 +1659,29 @@ proc insertSep*(s: string, sep = '_', digits = 3): string {.noSideEffect,
     dec(L)
 
 proc escape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect,
-  rtl, extern: "nsuEscape".} =
+  rtl, extern: "nsuEscape", deprecated.} =
   ## Escapes a string `s`. See `system.addEscapedChar <system.html#addEscapedChar>`_
   ## for the escaping scheme.
   ##
   ## The resulting string is prefixed with `prefix` and suffixed with `suffix`.
   ## Both may be empty strings.
+  ##
+  ## **Warning:** This procedure is deprecated because it's to easy to missuse.
   result = newStringOfCap(s.len + s.len shr 2)
   result.add(prefix)
   for c in items(s):
-    result.addEscapedChar(c)
+    case c
+    of '\0'..'\31', '\127'..'\255':
+      add(result, "\\x")
+      add(result, toHex(ord(c), 2))
+    of '\\': add(result, "\\\\")
+    of '\'': add(result, "\\'")
+    of '\"': add(result, "\\\"")
+    else: add(result, c)
   add(result, suffix)
 
 proc unescape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect,
-  rtl, extern: "nsuUnescape".} =
+  rtl, extern: "nsuUnescape", deprecated.} =
   ## Unescapes a string `s`.
   ##
   ## This complements `escape <#escape>`_ as it performs the opposite
@@ -1834,15 +1689,19 @@ proc unescape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect,
   ##
   ## If `s` does not begin with ``prefix`` and end with ``suffix`` a
   ## ValueError exception will be raised.
+  ##
+  ## **Warning:** This procedure is deprecated because it's to easy to missuse.
   result = newStringOfCap(s.len)
   var i = prefix.len
   if not s.startsWith(prefix):
     raise newException(ValueError,
-                       "String does not start with a prefix of: " & prefix)
+                       "String does not start with: " & prefix)
   while true:
-    if i == s.len-suffix.len: break
-    case s[i]
-    of '\\':
+    if i >= s.len-suffix.len: break
+    if s[i] == '\\':
+      if i+1 >= s.len:
+        result.add('\\')
+        break
       case s[i+1]:
       of 'x':
         inc i, 2
@@ -1856,15 +1715,15 @@ proc unescape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect,
         result.add('\'')
       of '\"':
         result.add('\"')
-      else: result.add("\\" & s[i+1])
-      inc(i)
-    of '\0': break
+      else:
+        result.add("\\" & s[i+1])
+      inc(i, 2)
     else:
       result.add(s[i])
-    inc(i)
+      inc(i)
   if not s.endsWith(suffix):
     raise newException(ValueError,
-                       "String does not end with a suffix of: " & suffix)
+                       "String does not end in: " & suffix)
 
 proc validIdentifier*(s: string): bool {.noSideEffect,
   rtl, extern: "nsuValidIdentifier".} =
@@ -1874,7 +1733,7 @@ proc validIdentifier*(s: string): bool {.noSideEffect,
   ## and is followed by any number of characters of the set `IdentChars`.
   runnableExamples:
     doAssert "abc_def08".validIdentifier
-  if s[0] in IdentStartChars:
+  if s.len > 0 and s[0] in IdentStartChars:
     for i in 1..s.len-1:
       if s[i] notin IdentChars: return false
     return true
@@ -1893,7 +1752,7 @@ proc editDistance*(a, b: string): int {.noSideEffect,
 
   # strip common prefix:
   var s = 0
-  while a[s] == b[s] and a[s] != '\0':
+  while s < len1 and a[s] == b[s]:
     inc(s)
     dec(len1)
     dec(len2)
@@ -1966,8 +1825,6 @@ proc editDistance*(a, b: string): int {.noSideEffect,
       if x > c3: x = c3
       row[p] = x
   result = row[e]
-  #dealloc(row)
-
 
 # floating point formating:
 when not defined(js):
@@ -2076,7 +1933,7 @@ proc trimZeros*(x: var string) {.noSideEffect.} =
   var spl: seq[string]
   if x.contains('.') or x.contains(','):
     if x.contains('e'):
-      spl= x.split('e')
+      spl = x.split('e')
       x = spl[0]
     while x[x.high] == '0':
       x.setLen(x.len-1)
@@ -2294,9 +2151,8 @@ proc addf*(s: var string, formatstr: string, a: varargs[string, `$`]) {.
   var i = 0
   var num = 0
   while i < len(formatstr):
-    if formatstr[i] == '$':
-      case formatstr[i+1] # again we use the fact that strings
-                          # are zero-terminated here
+    if formatstr[i] == '$' and i+1 < len(formatstr):
+      case formatstr[i+1]
       of '#':
         if num > a.high: invalidFormatString()
         add s, a[num]
@@ -2310,7 +2166,7 @@ proc addf*(s: var string, formatstr: string, a: varargs[string, `$`]) {.
         inc(i) # skip $
         var negative = formatstr[i] == '-'
         if negative: inc i
-        while formatstr[i] in Digits:
+        while i < formatstr.len and formatstr[i] in Digits:
           j = j * 10 + ord(formatstr[i]) - ord('0')
           inc(i)
         let idx = if not negative: j-1 else: a.len-j
@@ -2322,7 +2178,7 @@ proc addf*(s: var string, formatstr: string, a: varargs[string, `$`]) {.
         var negative = formatstr[j] == '-'
         if negative: inc j
         var isNumber = 0
-        while formatstr[j] notin {'\0', '}'}:
+        while j < formatstr.len and formatstr[j] notin {'\0', '}'}:
           if formatstr[j] in Digits:
             k = k * 10 + ord(formatstr[j]) - ord('0')
             if isNumber == 0: isNumber = 1
@@ -2340,7 +2196,7 @@ proc addf*(s: var string, formatstr: string, a: varargs[string, `$`]) {.
         i = j+1
       of 'a'..'z', 'A'..'Z', '\128'..'\255', '_':
         var j = i+1
-        while formatstr[j] in PatternChars: inc(j)
+        while j < formatstr.len and formatstr[j] in PatternChars: inc(j)
         var x = findNormalized(substr(formatstr, i+1, j-1), a)
         if x >= 0 and x < high(a): add s, a[x+1]
         else: invalidFormatString()
@@ -2499,239 +2355,243 @@ proc removePrefix*(s: var string, prefix: string) {.
     s.delete(0, prefix.len - 1)
 
 when isMainModule:
-  doAssert align("abc", 4) == " abc"
-  doAssert align("a", 0) == "a"
-  doAssert align("1232", 6) == "  1232"
-  doAssert align("1232", 6, '#') == "##1232"
-
-  doAssert alignLeft("abc", 4) == "abc "
-  doAssert alignLeft("a", 0) == "a"
-  doAssert alignLeft("1232", 6) == "1232  "
-  doAssert alignLeft("1232", 6, '#') == "1232##"
-
-  let
-    inp = """ this is a long text --  muchlongerthan10chars and here
-               it goes"""
-    outp = " this is a\nlong text\n--\nmuchlongerthan10chars\nand here\nit goes"
-  doAssert wordWrap(inp, 10, false) == outp
-
-  let
-    longInp = """ThisIsOneVeryLongStringWhichWeWillSplitIntoEightSeparatePartsNow"""
-    longOutp = "ThisIsOn\neVeryLon\ngStringW\nhichWeWi\nllSplitI\nntoEight\nSeparate\nPartsNow"
-  doAssert wordWrap(longInp, 8, true) == longOutp
-
-  doAssert formatBiggestFloat(1234.567, ffDecimal, -1) == "1234.567000"
-  doAssert formatBiggestFloat(1234.567, ffDecimal, 0) == "1235."
-  doAssert formatBiggestFloat(1234.567, ffDecimal, 1) == "1234.6"
-  doAssert formatBiggestFloat(0.00000000001, ffDecimal, 11) == "0.00000000001"
-  doAssert formatBiggestFloat(0.00000000001, ffScientific, 1, ',') in
-                                                   ["1,0e-11", "1,0e-011"]
-  # bug #6589
-  doAssert formatFloat(123.456, ffScientific, precision = -1) == "1.234560e+02"
-
-  doAssert "$# $3 $# $#" % ["a", "b", "c"] == "a c b c"
-  doAssert "${1}12 ${-1}$2" % ["a", "b"] == "a12 bb"
-
-  block: # formatSize tests
-    doAssert formatSize((1'i64 shl 31) + (300'i64 shl 20)) == "2.293GiB"
-    doAssert formatSize((2.234*1024*1024).int) == "2.234MiB"
-    doAssert formatSize(4096) == "4KiB"
-    doAssert formatSize(4096, prefix=bpColloquial, includeSpace=true) == "4 kB"
-    doAssert formatSize(4096, includeSpace=true) == "4 KiB"
-    doAssert formatSize(5_378_934, prefix=bpColloquial, decimalSep=',') == "5,13MB"
-
-  doAssert "$animal eats $food." % ["animal", "The cat", "food", "fish"] ==
-           "The cat eats fish."
-
-  doAssert "-ld a-ldz -ld".replaceWord("-ld") == " a-ldz "
-  doAssert "-lda-ldz -ld abc".replaceWord("-ld") == "-lda-ldz  abc"
-
-  type MyEnum = enum enA, enB, enC, enuD, enE
-  doAssert parseEnum[MyEnum]("enu_D") == enuD
-
-  doAssert parseEnum("invalid enum value", enC) == enC
-
-  doAssert center("foo", 13) == "     foo     "
-  doAssert center("foo", 0) == "foo"
-  doAssert center("foo", 3, fillChar = 'a') == "foo"
-  doAssert center("foo", 10, fillChar = '\t') == "\t\t\tfoo\t\t\t\t"
-
-  doAssert count("foofoofoo", "foofoo") == 1
-  doAssert count("foofoofoo", "foofoo", overlapping = true) == 2
-  doAssert count("foofoofoo", 'f') == 3
-  doAssert count("foofoofoobar", {'f','b'}) == 4
-
-  doAssert strip("  foofoofoo  ") == "foofoofoo"
-  doAssert strip("sfoofoofoos", chars = {'s'}) == "foofoofoo"
-  doAssert strip("barfoofoofoobar", chars = {'b', 'a', 'r'}) == "foofoofoo"
-  doAssert strip("stripme but don't strip this stripme",
-                 chars = {'s', 't', 'r', 'i', 'p', 'm', 'e'}) ==
-                 " but don't strip this "
-  doAssert strip("sfoofoofoos", leading = false, chars = {'s'}) == "sfoofoofoo"
-  doAssert strip("sfoofoofoos", trailing = false, chars = {'s'}) == "foofoofoos"
-
-  doAssert "  foo\n  bar".indent(4, "Q") == "QQQQ  foo\nQQQQ  bar"
-
-  doAssert "abba".multiReplace(("a", "b"), ("b", "a")) == "baab"
-  doAssert "Hello World.".multiReplace(("ello", "ELLO"), ("World.", "PEOPLE!")) == "HELLO PEOPLE!"
-  doAssert "aaaa".multiReplace(("a", "aa"), ("aa", "bb")) == "aaaaaaaa"
-
-  doAssert isAlphaAscii('r')
-  doAssert isAlphaAscii('A')
-  doAssert(not isAlphaAscii('$'))
-
-  doAssert isAlphaAscii("Rasp")
-  doAssert isAlphaAscii("Args")
-  doAssert(not isAlphaAscii("$Tomato"))
-
-  doAssert isAlphaNumeric('3')
-  doAssert isAlphaNumeric('R')
-  doAssert(not isAlphaNumeric('!'))
-
-  doAssert isAlphaNumeric("34ABc")
-  doAssert isAlphaNumeric("Rad")
-  doAssert isAlphaNumeric("1234")
-  doAssert(not isAlphaNumeric("@nose"))
-
-  doAssert isDigit('3')
-  doAssert(not isDigit('a'))
-  doAssert(not isDigit('%'))
-
-  doAssert isDigit("12533")
-  doAssert(not isDigit("12.33"))
-  doAssert(not isDigit("A45b"))
-
-  doAssert isSpaceAscii('\t')
-  doAssert isSpaceAscii('\l')
-  doAssert(not isSpaceAscii('A'))
-
-  doAssert isSpaceAscii("\t\l \v\r\f")
-  doAssert isSpaceAscii("       ")
-  doAssert(not isSpaceAscii("ABc   \td"))
-
-  doAssert(isNilOrEmpty(""))
-  doAssert(isNilOrEmpty(nil))
-  doAssert(not isNilOrEmpty("test"))
-  doAssert(not isNilOrEmpty(" "))
-
-  doAssert(isNilOrWhitespace(""))
-  doAssert(isNilOrWhitespace(nil))
-  doAssert(isNilOrWhitespace("       "))
-  doAssert(isNilOrWhitespace("\t\l \v\r\f"))
-  doAssert(not isNilOrWhitespace("ABc   \td"))
-
-  doAssert isLowerAscii('a')
-  doAssert isLowerAscii('z')
-  doAssert(not isLowerAscii('A'))
-  doAssert(not isLowerAscii('5'))
-  doAssert(not isLowerAscii('&'))
-
-  doAssert isLowerAscii("abcd")
-  doAssert(not isLowerAscii("abCD"))
-  doAssert(not isLowerAscii("33aa"))
-
-  doAssert isUpperAscii('A')
-  doAssert(not isUpperAscii('b'))
-  doAssert(not isUpperAscii('5'))
-  doAssert(not isUpperAscii('%'))
-
-  doAssert isUpperAscii("ABC")
-  doAssert(not isUpperAscii("AAcc"))
-  doAssert(not isUpperAscii("A#$"))
-
-  doAssert rsplit("foo bar", seps=Whitespace) == @["foo", "bar"]
-  doAssert rsplit(" foo bar", seps=Whitespace, maxsplit=1) == @[" foo", "bar"]
-  doAssert rsplit(" foo bar ", seps=Whitespace, maxsplit=1) == @[" foo bar", ""]
-  doAssert rsplit(":foo:bar", sep=':') == @["", "foo", "bar"]
-  doAssert rsplit(":foo:bar", sep=':', maxsplit=2) == @["", "foo", "bar"]
-  doAssert rsplit(":foo:bar", sep=':', maxsplit=3) == @["", "foo", "bar"]
-  doAssert rsplit("foothebar", sep="the") == @["foo", "bar"]
-
-  doAssert(unescape(r"\x013", "", "") == "\x013")
-
-  doAssert join(["foo", "bar", "baz"]) == "foobarbaz"
-  doAssert join(@["foo", "bar", "baz"], ", ") == "foo, bar, baz"
-  doAssert join([1, 2, 3]) == "123"
-  doAssert join(@[1, 2, 3], ", ") == "1, 2, 3"
-
-  doAssert """~~!!foo
+  proc nonStaticTests =
+    doAssert formatBiggestFloat(1234.567, ffDecimal, -1) == "1234.567000"
+    doAssert formatBiggestFloat(1234.567, ffDecimal, 0) == "1235."
+    doAssert formatBiggestFloat(1234.567, ffDecimal, 1) == "1234.6"
+    doAssert formatBiggestFloat(0.00000000001, ffDecimal, 11) == "0.00000000001"
+    doAssert formatBiggestFloat(0.00000000001, ffScientific, 1, ',') in
+                                                     ["1,0e-11", "1,0e-011"]
+    # bug #6589
+    doAssert formatFloat(123.456, ffScientific, precision = -1) == "1.234560e+02"
+
+    doAssert "$# $3 $# $#" % ["a", "b", "c"] == "a c b c"
+    doAssert "${1}12 ${-1}$2" % ["a", "b"] == "a12 bb"
+
+    block: # formatSize tests
+      doAssert formatSize((1'i64 shl 31) + (300'i64 shl 20)) == "2.293GiB"
+      doAssert formatSize((2.234*1024*1024).int) == "2.234MiB"
+      doAssert formatSize(4096) == "4KiB"
+      doAssert formatSize(4096, prefix=bpColloquial, includeSpace=true) == "4 kB"
+      doAssert formatSize(4096, includeSpace=true) == "4 KiB"
+      doAssert formatSize(5_378_934, prefix=bpColloquial, decimalSep=',') == "5,13MB"
+
+    block: # formatEng tests
+      doAssert formatEng(0, 2, trim=false) == "0.00"
+      doAssert formatEng(0, 2) == "0"
+      doAssert formatEng(53, 2, trim=false) == "53.00"
+      doAssert formatEng(0.053, 2, trim=false) == "53.00e-3"
+      doAssert formatEng(0.053, 4, trim=false) == "53.0000e-3"
+      doAssert formatEng(0.053, 4, trim=true) == "53e-3"
+      doAssert formatEng(0.053, 0) == "53e-3"
+      doAssert formatEng(52731234) == "52.731234e6"
+      doAssert formatEng(-52731234) == "-52.731234e6"
+      doAssert formatEng(52731234, 1) == "52.7e6"
+      doAssert formatEng(-52731234, 1) == "-52.7e6"
+      doAssert formatEng(52731234, 1, decimalSep=',') == "52,7e6"
+      doAssert formatEng(-52731234, 1, decimalSep=',') == "-52,7e6"
+
+      doAssert formatEng(4100, siPrefix=true, unit="V") == "4.1 kV"
+      doAssert formatEng(4.1, siPrefix=true, unit="V", useUnitSpace=true) == "4.1 V"
+      doAssert formatEng(4.1, siPrefix=true) == "4.1" # Note lack of space
+      doAssert formatEng(4100, siPrefix=true) == "4.1 k"
+      doAssert formatEng(4.1, siPrefix=true, unit="", useUnitSpace=true) == "4.1 " # Includes space
+      doAssert formatEng(4100, siPrefix=true, unit="") == "4.1 k"
+      doAssert formatEng(4100) == "4.1e3"
+      doAssert formatEng(4100, unit="V", useUnitSpace=true) == "4.1e3 V"
+      doAssert formatEng(4100, unit="", useUnitSpace=true) == "4.1e3 "
+      # Don't use SI prefix as number is too big
+      doAssert formatEng(3.1e22, siPrefix=true, unit="a", useUnitSpace=true) == "31e21 a"
+      # Don't use SI prefix as number is too small
+      doAssert formatEng(3.1e-25, siPrefix=true, unit="A", useUnitSpace=true) == "310e-27 A"
+
+  proc staticTests =
+    doAssert align("abc", 4) == " abc"
+    doAssert align("a", 0) == "a"
+    doAssert align("1232", 6) == "  1232"
+    doAssert align("1232", 6, '#') == "##1232"
+
+    doAssert alignLeft("abc", 4) == "abc "
+    doAssert alignLeft("a", 0) == "a"
+    doAssert alignLeft("1232", 6) == "1232  "
+    doAssert alignLeft("1232", 6, '#') == "1232##"
+
+    let
+      inp = """ this is a long text --  muchlongerthan10chars and here
+                 it goes"""
+      outp = " this is a\nlong text\n--\nmuchlongerthan10chars\nand here\nit goes"
+    doAssert wordWrap(inp, 10, false) == outp
+
+    let
+      longInp = """ThisIsOneVeryLongStringWhichWeWillSplitIntoEightSeparatePartsNow"""
+      longOutp = "ThisIsOn\neVeryLon\ngStringW\nhichWeWi\nllSplitI\nntoEight\nSeparate\nPartsNow"
+    doAssert wordWrap(longInp, 8, true) == longOutp
+
+    doAssert "$animal eats $food." % ["animal", "The cat", "food", "fish"] ==
+             "The cat eats fish."
+
+    doAssert "-ld a-ldz -ld".replaceWord("-ld") == " a-ldz "
+    doAssert "-lda-ldz -ld abc".replaceWord("-ld") == "-lda-ldz  abc"
+
+    doAssert "-lda-ldz -ld abc".replaceWord("") == "-lda-ldz -ld abc"
+    doAssert "oo".replace("", "abc") == "abcoabcoabc"
+
+    type MyEnum = enum enA, enB, enC, enuD, enE
+    doAssert parseEnum[MyEnum]("enu_D") == enuD
+
+    doAssert parseEnum("invalid enum value", enC) == enC
+
+    doAssert center("foo", 13) == "     foo     "
+    doAssert center("foo", 0) == "foo"
+    doAssert center("foo", 3, fillChar = 'a') == "foo"
+    doAssert center("foo", 10, fillChar = '\t') == "\t\t\tfoo\t\t\t\t"
+
+    doAssert count("foofoofoo", "foofoo") == 1
+    doAssert count("foofoofoo", "foofoo", overlapping = true) == 2
+    doAssert count("foofoofoo", 'f') == 3
+    doAssert count("foofoofoobar", {'f','b'}) == 4
+
+    doAssert strip("  foofoofoo  ") == "foofoofoo"
+    doAssert strip("sfoofoofoos", chars = {'s'}) == "foofoofoo"
+    doAssert strip("barfoofoofoobar", chars = {'b', 'a', 'r'}) == "foofoofoo"
+    doAssert strip("stripme but don't strip this stripme",
+                   chars = {'s', 't', 'r', 'i', 'p', 'm', 'e'}) ==
+                   " but don't strip this "
+    doAssert strip("sfoofoofoos", leading = false, chars = {'s'}) == "sfoofoofoo"
+    doAssert strip("sfoofoofoos", trailing = false, chars = {'s'}) == "foofoofoos"
+
+    doAssert "  foo\n  bar".indent(4, "Q") == "QQQQ  foo\nQQQQ  bar"
+
+    doAssert "abba".multiReplace(("a", "b"), ("b", "a")) == "baab"
+    doAssert "Hello World.".multiReplace(("ello", "ELLO"), ("World.", "PEOPLE!")) == "HELLO PEOPLE!"
+    doAssert "aaaa".multiReplace(("a", "aa"), ("aa", "bb")) == "aaaaaaaa"
+
+    doAssert isAlphaAscii('r')
+    doAssert isAlphaAscii('A')
+    doAssert(not isAlphaAscii('$'))
+
+    doAssert isAlphaAscii("Rasp")
+    doAssert isAlphaAscii("Args")
+    doAssert(not isAlphaAscii("$Tomato"))
+
+    doAssert isAlphaNumeric('3')
+    doAssert isAlphaNumeric('R')
+    doAssert(not isAlphaNumeric('!'))
+
+    doAssert isAlphaNumeric("34ABc")
+    doAssert isAlphaNumeric("Rad")
+    doAssert isAlphaNumeric("1234")
+    doAssert(not isAlphaNumeric("@nose"))
+
+    doAssert isDigit('3')
+    doAssert(not isDigit('a'))
+    doAssert(not isDigit('%'))
+
+    doAssert isDigit("12533")
+    doAssert(not isDigit("12.33"))
+    doAssert(not isDigit("A45b"))
+
+    doAssert isSpaceAscii('\t')
+    doAssert isSpaceAscii('\l')
+    doAssert(not isSpaceAscii('A'))
+
+    doAssert isSpaceAscii("\t\l \v\r\f")
+    doAssert isSpaceAscii("       ")
+    doAssert(not isSpaceAscii("ABc   \td"))
+
+    doAssert(isNilOrWhitespace(""))
+    doAssert(isNilOrWhitespace("       "))
+    doAssert(isNilOrWhitespace("\t\l \v\r\f"))
+    doAssert(not isNilOrWhitespace("ABc   \td"))
+
+    doAssert isLowerAscii('a')
+    doAssert isLowerAscii('z')
+    doAssert(not isLowerAscii('A'))
+    doAssert(not isLowerAscii('5'))
+    doAssert(not isLowerAscii('&'))
+
+    doAssert isLowerAscii("abcd")
+    doAssert(not isLowerAscii("abCD"))
+    doAssert(not isLowerAscii("33aa"))
+
+    doAssert isUpperAscii('A')
+    doAssert(not isUpperAscii('b'))
+    doAssert(not isUpperAscii('5'))
+    doAssert(not isUpperAscii('%'))
+
+    doAssert isUpperAscii("ABC")
+    doAssert(not isUpperAscii("AAcc"))
+    doAssert(not isUpperAscii("A#$"))
+
+    doAssert rsplit("foo bar", seps=Whitespace) == @["foo", "bar"]
+    doAssert rsplit(" foo bar", seps=Whitespace, maxsplit=1) == @[" foo", "bar"]
+    doAssert rsplit(" foo bar ", seps=Whitespace, maxsplit=1) == @[" foo bar", ""]
+    doAssert rsplit(":foo:bar", sep=':') == @["", "foo", "bar"]
+    doAssert rsplit(":foo:bar", sep=':', maxsplit=2) == @["", "foo", "bar"]
+    doAssert rsplit(":foo:bar", sep=':', maxsplit=3) == @["", "foo", "bar"]
+    doAssert rsplit("foothebar", sep="the") == @["foo", "bar"]
+
+    doAssert(unescape(r"\x013", "", "") == "\x013")
+
+    doAssert join(["foo", "bar", "baz"]) == "foobarbaz"
+    doAssert join(@["foo", "bar", "baz"], ", ") == "foo, bar, baz"
+    doAssert join([1, 2, 3]) == "123"
+    doAssert join(@[1, 2, 3], ", ") == "1, 2, 3"
+
+    doAssert """~~!!foo
 ~~!!bar
 ~~!!baz""".unindent(2, "~~!!") == "foo\nbar\nbaz"
 
-  doAssert """~~!!foo
+    doAssert """~~!!foo
 ~~!!bar
 ~~!!baz""".unindent(2, "~~!!aa") == "~~!!foo\n~~!!bar\n~~!!baz"
-  doAssert """~~foo
+    doAssert """~~foo
 ~~  bar
 ~~  baz""".unindent(4, "~") == "foo\n  bar\n  baz"
-  doAssert """foo
+    doAssert """foo
 bar
     baz
   """.unindent(4) == "foo\nbar\nbaz\n"
-  doAssert """foo
+    doAssert """foo
     bar
     baz
   """.unindent(2) == "foo\n  bar\n  baz\n"
-  doAssert """foo
+    doAssert """foo
     bar
     baz
   """.unindent(100) == "foo\nbar\nbaz\n"
 
-  doAssert """foo
+    doAssert """foo
     foo
     bar
   """.unindent() == "foo\nfoo\nbar\n"
 
-  let s = " this is an example  "
-  let s2 = ":this;is;an:example;;"
-
-  doAssert s.split() == @["", "this", "is", "an", "example", "", ""]
-  doAssert s2.split(seps={':', ';'}) == @["", "this", "is", "an", "example", "", ""]
-  doAssert s.split(maxsplit=4) == @["", "this", "is", "an", "example  "]
-  doAssert s.split(' ', maxsplit=1) == @["", "this is an example  "]
-  doAssert s.split(" ", maxsplit=4) == @["", "this", "is", "an", "example  "]
-
-  doAssert s.splitWhitespace() == @["this", "is", "an", "example"]
-  doAssert s.splitWhitespace(maxsplit=1) == @["this", "is an example  "]
-  doAssert s.splitWhitespace(maxsplit=2) == @["this", "is", "an example  "]
-  doAssert s.splitWhitespace(maxsplit=3) == @["this", "is", "an", "example  "]
-  doAssert s.splitWhitespace(maxsplit=4) == @["this", "is", "an", "example"]
-
-  block: # formatEng tests
-    doAssert formatEng(0, 2, trim=false) == "0.00"
-    doAssert formatEng(0, 2) == "0"
-    doAssert formatEng(53, 2, trim=false) == "53.00"
-    doAssert formatEng(0.053, 2, trim=false) == "53.00e-3"
-    doAssert formatEng(0.053, 4, trim=false) == "53.0000e-3"
-    doAssert formatEng(0.053, 4, trim=true) == "53e-3"
-    doAssert formatEng(0.053, 0) == "53e-3"
-    doAssert formatEng(52731234) == "52.731234e6"
-    doAssert formatEng(-52731234) == "-52.731234e6"
-    doAssert formatEng(52731234, 1) == "52.7e6"
-    doAssert formatEng(-52731234, 1) == "-52.7e6"
-    doAssert formatEng(52731234, 1, decimalSep=',') == "52,7e6"
-    doAssert formatEng(-52731234, 1, decimalSep=',') == "-52,7e6"
-
-    doAssert formatEng(4100, siPrefix=true, unit="V") == "4.1 kV"
-    doAssert formatEng(4.1, siPrefix=true, unit="V", useUnitSpace=true) == "4.1 V"
-    doAssert formatEng(4.1, siPrefix=true) == "4.1" # Note lack of space
-    doAssert formatEng(4100, siPrefix=true) == "4.1 k"
-    doAssert formatEng(4.1, siPrefix=true, unit="", useUnitSpace=true) == "4.1 " # Includes space
-    doAssert formatEng(4100, siPrefix=true, unit="") == "4.1 k"
-    doAssert formatEng(4100) == "4.1e3"
-    doAssert formatEng(4100, unit="V", useUnitSpace=true) == "4.1e3 V"
-    doAssert formatEng(4100, unit="", useUnitSpace=true) == "4.1e3 "
-    # Don't use SI prefix as number is too big
-    doAssert formatEng(3.1e22, siPrefix=true, unit="a", useUnitSpace=true) == "31e21 a"
-    # Don't use SI prefix as number is too small
-    doAssert formatEng(3.1e-25, siPrefix=true, unit="A", useUnitSpace=true) == "310e-27 A"
-
-  block: # startsWith / endsWith char tests
-    var s = "abcdef"
-    doAssert s.startsWith('a')
-    doAssert s.startsWith('b') == false
-    doAssert s.endsWith('f')
-    doAssert s.endsWith('a') == false
-    doAssert s.endsWith('\0') == false
-
-  #echo("strutils tests passed")
+    let s = " this is an example  "
+    let s2 = ":this;is;an:example;;"
+
+    doAssert s.split() == @["", "this", "is", "an", "example", "", ""]
+    doAssert s2.split(seps={':', ';'}) == @["", "this", "is", "an", "example", "", ""]
+    doAssert s.split(maxsplit=4) == @["", "this", "is", "an", "example  "]
+    doAssert s.split(' ', maxsplit=1) == @["", "this is an example  "]
+    doAssert s.split(" ", maxsplit=4) == @["", "this", "is", "an", "example  "]
+
+    doAssert s.splitWhitespace() == @["this", "is", "an", "example"]
+    doAssert s.splitWhitespace(maxsplit=1) == @["this", "is an example  "]
+    doAssert s.splitWhitespace(maxsplit=2) == @["this", "is", "an example  "]
+    doAssert s.splitWhitespace(maxsplit=3) == @["this", "is", "an", "example  "]
+    doAssert s.splitWhitespace(maxsplit=4) == @["this", "is", "an", "example"]
+
+    block: # startsWith / endsWith char tests
+      var s = "abcdef"
+      doAssert s.startsWith('a')
+      doAssert s.startsWith('b') == false
+      doAssert s.endsWith('f')
+      doAssert s.endsWith('a') == false
+      doAssert s.endsWith('\0') == false
+
+    #echo("strutils tests passed")
+
+  nonStaticTests()
+  staticTests()
+  static: staticTests()
+
diff --git a/lib/pure/subexes.nim b/lib/pure/subexes.nim
index 9d807abd4..8149c72cc 100644
--- a/lib/pure/subexes.nim
+++ b/lib/pure/subexes.nim
@@ -31,8 +31,6 @@ type
   SubexError* = object of ValueError ## exception that is raised for
                                      ## an invalid subex
 
-{.deprecated: [EInvalidSubex: SubexError].}
-
 proc raiseInvalidFormat(msg: string) {.noinline.} =
   raise newException(SubexError, "invalid format string: " & msg)
 
@@ -44,7 +42,6 @@ type
     else:
       f: cstring
     num, i, lineLen: int
-{.deprecated: [TFormatParser: FormatParser].}
 
 template call(x: untyped): untyped =
   p.i = i
diff --git a/lib/pure/terminal.nim b/lib/pure/terminal.nim
index 4f2f73ba7..fcca4d5d7 100644
--- a/lib/pure/terminal.nim
+++ b/lib/pure/terminal.nim
@@ -29,9 +29,7 @@ when not hasThreadSupport:
   var
     colorsFGCache = initTable[Color, string]()
     colorsBGCache = initTable[Color, string]()
-  when not defined(windows):
-    var
-      styleCache = initTable[int, string]()
+    styleCache = initTable[int, string]()
 
 var
   trueColorIsSupported: bool
@@ -41,10 +39,8 @@ var
 const
   fgPrefix = "\x1b[38;2;"
   bgPrefix = "\x1b[48;2;"
-
-when not defined(windows):
-  const
-    stylePrefix = "\e["
+  ansiResetCode* = "\e[0m"
+  stylePrefix = "\e["
 
 when defined(windows):
   import winlean, os
@@ -468,7 +464,7 @@ proc resetAttributes*(f: File) =
     else:
       discard setConsoleTextAttribute(hStdout, oldStdoutAttr)
   else:
-    f.write("\e[0m")
+    f.write(ansiResetCode)
 
 type
   Style* = enum         ## different styles for text output
@@ -487,15 +483,22 @@ when not defined(windows):
     gFG {.threadvar.}: int
     gBG {.threadvar.}: int
 
-  proc getStyleStr(style: int): string =
-    when hasThreadSupport:
-      result = fmt"{stylePrefix}{style}m"
+proc ansiStyleCode*(style: int): string =
+  when hasThreadSupport:
+    result = fmt"{stylePrefix}{style}m"
+  else:
+    if styleCache.hasKey(style):
+      result = styleCache[style]
     else:
-      if styleCache.hasKey(style):
-        result = styleCache[style]
-      else:
-        result = fmt"{stylePrefix}{style}m"
-        styleCache[style] = result
+      result = fmt"{stylePrefix}{style}m"
+      styleCache[style] = result
+
+template ansiStyleCode*(style: Style): string =
+  ansiStyleCode(style.int)
+
+# The styleCache can be skipped when `style` is known at compile-time
+template ansiStyleCode*(style: static[Style]): string =
+  (static(stylePrefix & $style.int & "m"))
 
 proc setStyle*(f: File, style: set[Style]) =
   ## Sets the terminal style.
@@ -510,7 +513,7 @@ proc setStyle*(f: File, style: set[Style]) =
     discard setConsoleTextAttribute(h, old or a)
   else:
     for s in items(style):
-      f.write(getStyleStr(ord(s)))
+      f.write(ansiStyleCode(s))
 
 proc writeStyled*(txt: string, style: set[Style] = {styleBright}) =
   ## Writes the text `txt` in a given `style` to stdout.
@@ -524,9 +527,9 @@ proc writeStyled*(txt: string, style: set[Style] = {styleBright}) =
     stdout.write(txt)
     stdout.resetAttributes()
     if gFG != 0:
-      stdout.write(getStyleStr(gFG))
+      stdout.write(ansiStyleCode(gFG))
     if gBG != 0:
-      stdout.write(getStyleStr(gBG))
+      stdout.write(ansiStyleCode(gBG))
 
 type
   ForegroundColor* = enum  ## terminal's foreground colors
@@ -557,8 +560,8 @@ proc setForegroundColor*(f: File, fg: ForegroundColor, bright=false) =
   when defined(windows):
     let h = conHandle(f)
     var old = getAttributes(h) and not FOREGROUND_RGB
-    if bright:
-      old = old or FOREGROUND_INTENSITY
+    old = if bright: old or FOREGROUND_INTENSITY
+          else:      old and not(FOREGROUND_INTENSITY)
     const lookup: array[ForegroundColor, int] = [
       0,
       (FOREGROUND_RED),
@@ -572,15 +575,15 @@ proc setForegroundColor*(f: File, fg: ForegroundColor, bright=false) =
   else:
     gFG = ord(fg)
     if bright: inc(gFG, 60)
-    f.write(getStyleStr(gFG))
+    f.write(ansiStyleCode(gFG))
 
 proc setBackgroundColor*(f: File, bg: BackgroundColor, bright=false) =
   ## Sets the terminal's background color.
   when defined(windows):
     let h = conHandle(f)
     var old = getAttributes(h) and not BACKGROUND_RGB
-    if bright:
-      old = old or BACKGROUND_INTENSITY
+    old = if bright: old or BACKGROUND_INTENSITY
+          else:      old and not(BACKGROUND_INTENSITY)
     const lookup: array[BackgroundColor, int] = [
       0,
       (BACKGROUND_RED),
@@ -594,10 +597,18 @@ proc setBackgroundColor*(f: File, bg: BackgroundColor, bright=false) =
   else:
     gBG = ord(bg)
     if bright: inc(gBG, 60)
-    f.write(getStyleStr(gBG))
+    f.write(ansiStyleCode(gBG))
+
+proc ansiForegroundColorCode*(fg: ForegroundColor, bright=false): string =
+  var style = ord(fg)
+  if bright: inc(style, 60)
+  return ansiStyleCode(style)
 
+template ansiForegroundColorCode*(fg: static[ForegroundColor],
+                                  bright: static[bool] = false): string =
+  ansiStyleCode(fg.int + bright.int * 60)
 
-proc getFGColorStr(color: Color): string =
+proc ansiForegroundColorCode*(color: Color): string =
   when hasThreadSupport:
     let rgb = extractRGB(color)
     result = fmt"{fgPrefix}{rgb.r};{rgb.g};{rgb.b}m"
@@ -609,7 +620,11 @@ proc getFGColorStr(color: Color): string =
       result = fmt"{fgPrefix}{rgb.r};{rgb.g};{rgb.b}m"
       colorsFGCache[color] = result
 
-proc getBGColorStr(color: Color): string =
+template ansiForegroundColorCode*(color: static[Color]): string =
+  const rgb = extractRGB(color)
+  (static(fmt"{fgPrefix}{rgb.r};{rgb.g};{rgb.b}m"))
+
+proc ansiBackgroundColorCode*(color: Color): string =
   when hasThreadSupport:
     let rgb = extractRGB(color)
     result = fmt"{bgPrefix}{rgb.r};{rgb.g};{rgb.b}m"
@@ -621,15 +636,19 @@ proc getBGColorStr(color: Color): string =
       result = fmt"{bgPrefix}{rgb.r};{rgb.g};{rgb.b}m"
       colorsFGCache[color] = result
 
+template ansiBackgroundColorCode*(color: static[Color]): string =
+  const rgb = extractRGB(color)
+  (static(fmt"{bgPrefix}{rgb.r};{rgb.g};{rgb.b}m"))
+
 proc setForegroundColor*(f: File, color: Color) =
   ## Sets the terminal's foreground true color.
   if trueColorIsEnabled:
-    f.write(getFGColorStr(color))
+    f.write(ansiForegroundColorCode(color))
 
 proc setBackgroundColor*(f: File, color: Color) =
   ## Sets the terminal's background true color.
   if trueColorIsEnabled:
-    f.write(getBGColorStr(color))
+    f.write(ansiBackgroundColorCode(color))
 
 proc setTrueColor(f: File, color: Color) =
   if fgSetColor:
@@ -759,6 +778,10 @@ when defined(windows):
           x = runeLenAt(password.string, i)
           inc i, x
         password.string.setLen(max(password.len - x, 0))
+      of chr(0x0):
+        # modifier key - ignore - for details see 
+        # https://github.com/nim-lang/Nim/issues/7764
+        continue
       else:
         password.string.add(toUTF8(c.Rune))
     stdout.write "\n"
diff --git a/lib/pure/times.nim b/lib/pure/times.nim
index dc216477b..f6567cf58 100644
--- a/lib/pure/times.nim
+++ b/lib/pure/times.nim
@@ -103,7 +103,7 @@ type
 
   Time* = object ## Represents a point in time.
     seconds: int64
-    nanoseconds: NanosecondRange
+    nanosecond: NanosecondRange
 
   DateTime* = object of RootObj ## Represents a time in different parts.
                                 ## Although this type can represent leap
@@ -159,7 +159,7 @@ type
                      ## This type should be prefered over ``TimeInterval`` unless
                      ## non-static time units is needed.
     seconds: int64
-    nanoseconds: NanosecondRange
+    nanosecond: NanosecondRange
 
   TimeUnit* = enum ## Different units of time.
     Nanoseconds, Microseconds, Milliseconds, Seconds, Minutes, Hours, Days, Weeks, Months, Years
@@ -191,6 +191,7 @@ const
   secondsInHour = 60*60
   secondsInDay = 60*60*24
   minutesInHour = 60
+  rateDiff = 10000000'i64 # 100 nsecs
   # The number of hectonanoseconds between 1601/01/01 (windows epoch)
   # and 1970/01/01 (unix epoch).
   epochDiff = 116444736000000000'i64
@@ -222,21 +223,21 @@ proc normalize[T: Duration|Time](seconds, nanoseconds: int64): T =
   ## a ``Duration`` or ``Time``. A normalized ``Duration|Time`` has a
   ## positive nanosecond part in the range ``NanosecondRange``.
   result.seconds = seconds + convert(Nanoseconds, Seconds, nanoseconds)
-  var nanoseconds = nanoseconds mod convert(Seconds, Nanoseconds, 1)
-  if nanoseconds < 0:
-    nanoseconds += convert(Seconds, Nanoseconds, 1)
+  var nanosecond = nanoseconds mod convert(Seconds, Nanoseconds, 1)
+  if nanosecond < 0:
+    nanosecond += convert(Seconds, Nanoseconds, 1)
     result.seconds -= 1
-  result.nanoseconds = nanoseconds.int
+  result.nanosecond = nanosecond.int
 
-proc initTime*(unix: int64, nanoseconds: NanosecondRange): Time =
+proc initTime*(unix: int64, nanosecond: NanosecondRange): Time =
   ## Create a ``Time`` from a unix timestamp and a nanosecond part.
   result.seconds = unix
-  result.nanoseconds = nanoseconds
+  result.nanosecond = nanosecond
 
-proc nanoseconds*(time: Time): NanosecondRange =
+proc nanosecond*(time: Time): NanosecondRange =
   ## Get the fractional part of a ``Time`` as the number
   ## of nanoseconds of the second.
-  time.nanoseconds
+  time.nanosecond
 
 proc initDuration*(nanoseconds, microseconds, milliseconds,
                    seconds, minutes, hours, days, weeks: int64 = 0): Duration =
@@ -280,7 +281,7 @@ proc milliseconds*(dur: Duration): int {.inline.} =
   runnableExamples:
     let dur = initDuration(seconds = 1, milliseconds = 1)
     doAssert dur.milliseconds == 1
-  convert(Nanoseconds, Milliseconds, dur.nanoseconds)
+  convert(Nanoseconds, Milliseconds, dur.nanosecond)
 
 proc microseconds*(dur: Duration): int {.inline.} =
   ## Number of whole microseconds represented by the **fractional**
@@ -288,7 +289,7 @@ proc microseconds*(dur: Duration): int {.inline.} =
   runnableExamples:
     let dur = initDuration(seconds = 1, microseconds = 1)
     doAssert dur.microseconds == 1
-  convert(Nanoseconds, Microseconds, dur.nanoseconds)
+  convert(Nanoseconds, Microseconds, dur.nanosecond)
 
 proc nanoseconds*(dur: Duration): int {.inline.} =
   ## Number of whole nanoseconds represented by the **fractional**
@@ -296,14 +297,14 @@ proc nanoseconds*(dur: Duration): int {.inline.} =
   runnableExamples:
     let dur = initDuration(seconds = 1, nanoseconds = 1)
     doAssert dur.nanoseconds == 1
-  dur.nanoseconds
+  dur.nanosecond
 
 proc fractional*(dur: Duration): Duration {.inline.} =
   ## The fractional part of duration, as a duration.
   runnableExamples:
     let dur = initDuration(seconds = 1, nanoseconds = 5)
     doAssert dur.fractional == initDuration(nanoseconds = 5)
-  initDuration(nanoseconds = dur.nanoseconds)
+  initDuration(nanoseconds = dur.nanosecond)
 
 const DurationZero* = initDuration() ## Zero value for durations. Useful for comparisons.
                                      ##
@@ -321,7 +322,7 @@ proc `$`*(dur: Duration): string =
     doAssert $initDuration(milliseconds = -1500) == "-1 second and -500 milliseconds"
   var parts = newSeq[string]()
   var remS = dur.seconds
-  var remNs = dur.nanoseconds.int
+  var remNs = dur.nanosecond.int
 
   # Normally ``nanoseconds`` should always be positive, but
   # that makes no sense when printing.
@@ -352,7 +353,9 @@ proc `$`*(dur: Duration): string =
       parts.add $quantity & " " & unitStrings[unit] & "s"
 
   result = ""
-  if parts.len == 1:
+  if parts.len == 0:
+    result.add "0 nanoseconds"
+  elif parts.len == 1:
     result = parts[0]
   elif parts.len == 2:
     result = parts[0] & " and " & parts[1]
@@ -371,6 +374,21 @@ proc toUnix*(t: Time): int64 {.benign, tags: [], raises: [], noSideEffect.} =
   ## Convert ``t`` to a unix timestamp (seconds since ``1970-01-01T00:00:00Z``).
   t.seconds
 
+proc fromWinTime*(win: int64): Time =
+  ## Convert a Windows file time (100-nanosecond intervals since ``1601-01-01T00:00:00Z``)
+  ## to a ``Time``.
+  let hnsecsSinceEpoch = (win - epochDiff)
+  var seconds = hnsecsSinceEpoch div rateDiff
+  var nanos = ((hnsecsSinceEpoch mod rateDiff) * 100).int
+  if nanos < 0:
+    nanos += convert(Seconds, Nanoseconds, 1)
+    seconds -= 1
+  result = initTime(seconds, nanos)
+
+proc toWinTime*(t: Time): int64 =
+  ## Convert ``t`` to a Windows file time (100-nanosecond intervals since ``1601-01-01T00:00:00Z``).
+  result = t.seconds * rateDiff + epochDiff + t.nanosecond div 100
+
 proc isLeapYear*(year: int): bool =
   ## Returns true if ``year`` is a leap year.
   year mod 4 == 0 and (year mod 100 != 0 or year mod 400 == 0)
@@ -455,21 +473,21 @@ proc localZoneInfoFromTz(adjTime: Time): ZonedTime {.tags: [], raises: [], benig
 {. pragma: operator, rtl, noSideEffect, benign .}
 
 template subImpl[T: Duration|Time](a: Duration|Time, b: Duration|Time): T =
-  normalize[T](a.seconds - b.seconds, a.nanoseconds - b.nanoseconds)
+  normalize[T](a.seconds - b.seconds, a.nanosecond - b.nanosecond)
 
 template addImpl[T: Duration|Time](a: Duration|Time, b: Duration|Time): T =
-  normalize[T](a.seconds + b.seconds, a.nanoseconds + b.nanoseconds)
+  normalize[T](a.seconds + b.seconds, a.nanosecond + b.nanosecond)
 
 template ltImpl(a: Duration|Time, b: Duration|Time): bool =
   a.seconds < b.seconds or (
-    a.seconds == b.seconds and a.nanoseconds < b.nanoseconds)
+    a.seconds == b.seconds and a.nanosecond < b.nanosecond)
 
 template lqImpl(a: Duration|Time, b: Duration|Time): bool =
-  a.seconds <= b.seconds or (
-    a.seconds == b.seconds and a.nanoseconds <= b.seconds)
+  a.seconds < b.seconds or (
+    a.seconds == b.seconds and a.nanosecond <= b.nanosecond)
 
 template eqImpl(a: Duration|Time, b: Duration|Time): bool =
-  a.seconds == b.seconds and a.nanoseconds == b.nanoseconds
+  a.seconds == b.seconds and a.nanosecond == b.nanosecond
 
 proc `+`*(a, b: Duration): Duration {.operator.} =
   ## Add two durations together.
@@ -489,7 +507,7 @@ proc `-`*(a: Duration): Duration {.operator.} =
   ## Reverse a duration.
   runnableExamples:
     doAssert -initDuration(seconds = 1) == initDuration(seconds = -1)
-  normalize[Duration](-a.seconds, -a.nanoseconds)
+  normalize[Duration](-a.seconds, -a.nanosecond)
 
 proc `<`*(a, b: Duration): bool {.operator.} =
   ## Note that a duration can be negative,
@@ -512,7 +530,7 @@ proc `*`*(a: int64, b: Duration): Duration {.operator} =
   ## Multiply a duration by some scalar.
   runnableExamples:
     doAssert 5 * initDuration(seconds = 1) == initDuration(seconds = 5)
-  normalize[Duration](a * b.seconds, a * b.nanoseconds)
+  normalize[Duration](a * b.seconds, a * b.nanosecond)
 
 proc `*`*(a: Duration, b: int64): Duration {.operator} =
   ## Multiply a duration by some scalar.
@@ -526,7 +544,7 @@ proc `div`*(a: Duration, b: int64): Duration {.operator} =
     doAssert initDuration(seconds = 3) div 2 == initDuration(milliseconds = 1500)
     doAssert initDuration(nanoseconds = 3) div 2 == initDuration(nanoseconds = 1)
   let carryOver = convert(Seconds, Nanoseconds, a.seconds mod b)
-  normalize[Duration](a.seconds div b, (a.nanoseconds + carryOver) div b)
+  normalize[Duration](a.seconds div b, (a.nanosecond + carryOver) div b)
 
 proc `-`*(a, b: Time): Duration {.operator, extern: "ntDiffTime".} =
   ## Computes the duration between two points in time.
@@ -582,7 +600,7 @@ proc abs*(a: Duration): Duration =
   runnableExamples:
     doAssert initDuration(milliseconds = -1500).abs ==
       initDuration(milliseconds = 1500)
-  initDuration(seconds = abs(a.seconds), nanoseconds = -a.nanoseconds)
+  initDuration(seconds = abs(a.seconds), nanoseconds = -a.nanosecond)
 
 proc toTime*(dt: DateTime): Time {.tags: [], raises: [], benign.} =
   ## Converts a broken-down time structure to
@@ -632,7 +650,7 @@ proc initDateTime(zt: ZonedTime, zone: Timezone): DateTime =
     hour: hour,
     minute: minute,
     second: second,
-    nanosecond: zt.adjTime.nanoseconds,
+    nanosecond: zt.adjTime.nanosecond,
     weekday: getDayOfWeek(d, m, y),
     yearday: getDayOfYear(d, m, y),
     isDst: zt.isDst,
@@ -791,7 +809,7 @@ else:
     # as a result of offset changes (normally due to dst)
     let utcUnix = adjTime.seconds + utcOffset
     let (finalOffset, dst) = getLocalOffsetAndDst(utcUnix)
-    result.adjTime = initTime(utcUnix - finalOffset, adjTime.nanoseconds)
+    result.adjTime = initTime(utcUnix - finalOffset, adjTime.nanosecond)
     result.utcOffset = finalOffset
     result.isDst = dst
 
@@ -853,10 +871,7 @@ proc getTime*(): Time {.tags: [TimeEffect], benign.} =
   elif defined(windows):
     var f: FILETIME
     getSystemTimeAsFileTime(f)
-    let nanosSinceEpoch = (rdFileTime(f) - epochDiff) * 100
-    let seconds = convert(Nanoseconds, Seconds, nanosSinceEpoch)
-    let nanos = (nanosSinceEpoch mod convert(Seconds, Nanoseconds, 1)).int
-    result = initTime(seconds, nanos)
+    result = fromWinTime(rdFileTime(f))
 
 proc now*(): DateTime {.tags: [TimeEffect], benign.} =
   ## Get the current time as a  ``DateTime`` in the local timezone.
@@ -1158,12 +1173,16 @@ proc formatToken(dt: DateTime, token: string, buf: var string) =
   of "dddd":
     buf.add($dt.weekday)
   of "h":
-    buf.add($(if dt.hour > 12: dt.hour - 12 else: dt.hour))
+    if dt.hour == 0: buf.add("12")
+    else: buf.add($(if dt.hour > 12: dt.hour - 12 else: dt.hour))
   of "hh":
-    let amerHour = if dt.hour > 12: dt.hour - 12 else: dt.hour
-    if amerHour < 10:
-      buf.add('0')
-    buf.add($amerHour)
+    if dt.hour == 0:
+      buf.add("12")
+    else:
+      let amerHour = if dt.hour > 12: dt.hour - 12 else: dt.hour
+      if amerHour < 10:
+        buf.add('0')
+      buf.add($amerHour)
   of "H":
     buf.add($dt.hour)
   of "HH":
@@ -1303,24 +1322,23 @@ proc format*(dt: DateTime, f: string): string {.tags: [].}=
   result = ""
   var i = 0
   var currentF = ""
-  while true:
+  while i < f.len:
     case f[i]
-    of ' ', '-', '/', ':', '\'', '\0', '(', ')', '[', ']', ',':
+    of ' ', '-', '/', ':', '\'', '(', ')', '[', ']', ',':
       formatToken(dt, currentF, result)
 
       currentF = ""
-      if f[i] == '\0': break
 
       if f[i] == '\'':
         inc(i) # Skip '
-        while f[i] != '\'' and f.len-1 > i:
+        while i < f.len-1 and f[i] != '\'':
           result.add(f[i])
           inc(i)
       else: result.add(f[i])
 
     else:
       # Check if the letter being added matches previous accumulated buffer.
-      if currentF.len < 1 or currentF[high(currentF)] == f[i]:
+      if currentF.len == 0 or currentF[high(currentF)] == f[i]:
         currentF.add(f[i])
       else:
         formatToken(dt, currentF, result)
@@ -1328,6 +1346,7 @@ proc format*(dt: DateTime, f: string): string {.tags: [].}=
         currentF = ""
 
     inc(i)
+  formatToken(dt, currentF, result)
 
 proc `$`*(dt: DateTime): string {.tags: [], raises: [], benign.} =
   ## Converts a `DateTime` object to a string representation.
@@ -1424,58 +1443,58 @@ proc parseToken(dt: var DateTime; token, value: string; j: var int) =
     dt.month = month.Month
   of "MMM":
     case value[j..j+2].toLowerAscii():
-    of "jan": dt.month =  mJan
-    of "feb": dt.month =  mFeb
-    of "mar": dt.month =  mMar
-    of "apr": dt.month =  mApr
-    of "may": dt.month =  mMay
-    of "jun": dt.month =  mJun
-    of "jul": dt.month =  mJul
-    of "aug": dt.month =  mAug
-    of "sep": dt.month =  mSep
-    of "oct": dt.month =  mOct
-    of "nov": dt.month =  mNov
-    of "dec": dt.month =  mDec
+    of "jan": dt.month = mJan
+    of "feb": dt.month = mFeb
+    of "mar": dt.month = mMar
+    of "apr": dt.month = mApr
+    of "may": dt.month = mMay
+    of "jun": dt.month = mJun
+    of "jul": dt.month = mJul
+    of "aug": dt.month = mAug
+    of "sep": dt.month = mSep
+    of "oct": dt.month = mOct
+    of "nov": dt.month = mNov
+    of "dec": dt.month = mDec
     else:
       raise newException(ValueError,
         "Couldn't parse month (MMM), got: " & value)
     j += 3
   of "MMMM":
     if value.len >= j+7 and value[j..j+6].cmpIgnoreCase("january") == 0:
-      dt.month =  mJan
+      dt.month = mJan
       j += 7
     elif value.len >= j+8 and value[j..j+7].cmpIgnoreCase("february") == 0:
-      dt.month =  mFeb
+      dt.month = mFeb
       j += 8
     elif value.len >= j+5 and value[j..j+4].cmpIgnoreCase("march") == 0:
-      dt.month =  mMar
+      dt.month = mMar
       j += 5
     elif value.len >= j+5 and value[j..j+4].cmpIgnoreCase("april") == 0:
-      dt.month =  mApr
+      dt.month = mApr
       j += 5
     elif value.len >= j+3 and value[j..j+2].cmpIgnoreCase("may") == 0:
-      dt.month =  mMay
+      dt.month = mMay
       j += 3
     elif value.len >= j+4 and value[j..j+3].cmpIgnoreCase("june") == 0:
-      dt.month =  mJun
+      dt.month = mJun
       j += 4
     elif value.len >= j+4 and value[j..j+3].cmpIgnoreCase("july") == 0:
-      dt.month =  mJul
+      dt.month = mJul
       j += 4
     elif value.len >= j+6 and value[j..j+5].cmpIgnoreCase("august") == 0:
-      dt.month =  mAug
+      dt.month = mAug
       j += 6
     elif value.len >= j+9 and value[j..j+8].cmpIgnoreCase("september") == 0:
-      dt.month =  mSep
+      dt.month = mSep
       j += 9
     elif value.len >= j+7 and value[j..j+6].cmpIgnoreCase("october") == 0:
-      dt.month =  mOct
+      dt.month = mOct
       j += 7
     elif value.len >= j+8 and value[j..j+7].cmpIgnoreCase("november") == 0:
-      dt.month =  mNov
+      dt.month = mNov
       j += 8
     elif value.len >= j+8 and value[j..j+7].cmpIgnoreCase("december") == 0:
-      dt.month =  mDec
+      dt.month = mDec
       j += 8
     else:
       raise newException(ValueError,
@@ -1488,11 +1507,15 @@ proc parseToken(dt: var DateTime; token, value: string; j: var int) =
     dt.second = value[j..j+1].parseInt()
     j += 2
   of "t":
-    if value[j] == 'P' and dt.hour > 0 and dt.hour < 12:
+    if value[j] == 'A' and dt.hour == 12:
+      dt.hour = 0
+    elif value[j] == 'P' and dt.hour > 0 and dt.hour < 12:
       dt.hour += 12
     j += 1
   of "tt":
-    if value[j..j+1] == "PM" and dt.hour > 0 and dt.hour < 12:
+    if value[j..j+1] == "AM" and dt.hour == 12:
+      dt.hour = 0
+    elif value[j..j+1] == "PM" and dt.hour > 0 and dt.hour < 12:
       dt.hour += 12
     j += 2
   of "yy":
@@ -1506,44 +1529,47 @@ proc parseToken(dt: var DateTime; token, value: string; j: var int) =
     j += 4
   of "z":
     dt.isDst = false
-    if value[j] == '+':
+    let ch = if j < value.len: value[j] else: '\0'
+    if ch == '+':
       dt.utcOffset = 0 - parseInt($value[j+1]) * secondsInHour
-    elif value[j] == '-':
+    elif ch == '-':
       dt.utcOffset = parseInt($value[j+1]) * secondsInHour
-    elif value[j] == 'Z':
+    elif ch == 'Z':
       dt.utcOffset = 0
       j += 1
       return
     else:
       raise newException(ValueError,
-        "Couldn't parse timezone offset (z), got: " & value[j])
+        "Couldn't parse timezone offset (z), got: " & ch)
     j += 2
   of "zz":
     dt.isDst = false
-    if value[j] == '+':
+    let ch = if j < value.len: value[j] else: '\0'
+    if ch == '+':
       dt.utcOffset = 0 - value[j+1..j+2].parseInt() * secondsInHour
-    elif value[j] == '-':
+    elif ch == '-':
       dt.utcOffset = value[j+1..j+2].parseInt() * secondsInHour
-    elif value[j] == 'Z':
+    elif ch == 'Z':
       dt.utcOffset = 0
       j += 1
       return
     else:
       raise newException(ValueError,
-        "Couldn't parse timezone offset (zz), got: " & value[j])
+        "Couldn't parse timezone offset (zz), got: " & ch)
     j += 3
   of "zzz":
     dt.isDst = false
     var factor = 0
-    if value[j] == '+': factor = -1
-    elif value[j] == '-': factor = 1
-    elif value[j] == 'Z':
+    let ch = if j < value.len: value[j] else: '\0'
+    if ch == '+': factor = -1
+    elif ch == '-': factor = 1
+    elif ch == 'Z':
       dt.utcOffset = 0
       j += 1
       return
     else:
       raise newException(ValueError,
-        "Couldn't parse timezone offset (zzz), got: " & value[j])
+        "Couldn't parse timezone offset (zzz), got: " & ch)
     dt.utcOffset = factor * value[j+1..j+2].parseInt() * secondsInHour
     j += 4
     dt.utcOffset += factor * value[j..j+1].parseInt() * 60
@@ -1605,20 +1631,18 @@ proc parse*(value, layout: string, zone: Timezone = local()): DateTime =
   dt.nanosecond = 0
   dt.isDst = true # using this is flag for checking whether a timezone has \
       # been read (because DST is always false when a tz is parsed)
-  while true:
+  while i < layout.len:
     case layout[i]
-    of ' ', '-', '/', ':', '\'', '\0', '(', ')', '[', ']', ',':
+    of ' ', '-', '/', ':', '\'', '(', ')', '[', ']', ',':
       if token.len > 0:
         parseToken(dt, token, value, j)
       # Reset token
       token = ""
-      # Break if at end of line
-      if layout[i] == '\0': break
       # Skip separator and everything between single quotes
       # These are literals in both the layout and the value string
       if layout[i] == '\'':
         inc(i)
-        while layout[i] != '\'' and layout.len-1 > i:
+        while i < layout.len-1 and layout[i] != '\'':
           inc(i)
           inc(j)
         inc(i)
@@ -1627,13 +1651,15 @@ proc parse*(value, layout: string, zone: Timezone = local()): DateTime =
         inc(j)
     else:
       # Check if the letter being added matches previous accumulated buffer.
-      if token.len < 1 or token[high(token)] == layout[i]:
+      if token.len == 0 or token[high(token)] == layout[i]:
         token.add(layout[i])
         inc(i)
       else:
         parseToken(dt, token, value, j)
         token = ""
 
+  if i >= layout.len and token.len > 0:
+    parseToken(dt, token, value, j)
   if dt.isDst:
     # No timezone parsed - assume timezone is `zone`
     result = initDateTime(zone.zoneInfoFromTz(dt.toAdjTime), zone)
@@ -1713,17 +1739,6 @@ when not defined(JS):
   var
     clocksPerSec {.importc: "CLOCKS_PER_SEC", nodecl.}: int
 
-  const
-    rateDiff = 10000000'i64 # 100 nsecs
-
-  proc unixTimeToWinTime*(time: CTime): int64 =
-    ## converts a UNIX `Time` (``time_t``) to a Windows file time
-    result = int64(time) * rateDiff + epochDiff
-
-  proc winTimeToUnixTime*(time: int64): CTime =
-    ## converts a Windows time to a UNIX `Time` (``time_t``)
-    result = CTime((time - epochDiff) div rateDiff)
-
   when not defined(useNimRtl):
     proc cpuTime*(): float {.rtl, extern: "nt$1", tags: [TimeEffect].} =
       ## gets time spent that the CPU spent to run the current process in
@@ -1748,7 +1763,7 @@ when not defined(JS):
       when defined(posix):
         var a: Timeval
         gettimeofday(a)
-        result = toFloat(a.tv_sec) + toFloat(a.tv_usec)*0.00_0001
+        result = toBiggestFloat(a.tv_sec.int64) + toFloat(a.tv_usec)*0.00_0001
       elif defined(windows):
         var f: winlean.FILETIME
         getSystemTimeAsFileTime(f)
@@ -1765,6 +1780,19 @@ when defined(JS):
 
 # Deprecated procs
 
+when not defined(JS):
+  proc unixTimeToWinTime*(time: CTime): int64 {.deprecated: "Use toWinTime instead".} =
+    ## Converts a UNIX `Time` (``time_t``) to a Windows file time
+    ##
+    ## **Deprecated:** use ``toWinTime`` instead.
+    result = int64(time) * rateDiff + epochDiff
+
+  proc winTimeToUnixTime*(time: int64): CTime {.deprecated: "Use fromWinTime instead".} =
+    ## Converts a Windows time to a UNIX `Time` (``time_t``)
+    ##
+    ## **Deprecated:** use ``fromWinTime`` instead.
+    result = CTime((time - epochDiff) div rateDiff)
+
 proc initInterval*(seconds, minutes, hours, days, months,
                    years: int = 0): TimeInterval {.deprecated.} =
   ## **Deprecated since v0.18.0:** use ``initTimeInterval`` instead.
@@ -1789,7 +1817,7 @@ proc toSeconds*(time: Time): float {.tags: [], raises: [], benign, deprecated.}
   ## Returns the time in seconds since the unix epoch.
   ##
   ## **Deprecated since v0.18.0:** use ``fromUnix`` instead
-  time.seconds.float + time.nanoseconds / convert(Seconds, Nanoseconds, 1)
+  time.seconds.float + time.nanosecond / convert(Seconds, Nanoseconds, 1)
 
 proc getLocalTime*(time: Time): DateTime {.tags: [], raises: [], benign, deprecated.} =
   ## Converts the calendar time `time` to broken-time representation,
@@ -1836,7 +1864,7 @@ when defined(JS):
     ## version 0.8.10.** Use ``epochTime`` or ``cpuTime`` instead.
     let dur = getTime() - start
     result = (convert(Seconds, Milliseconds, dur.seconds) +
-      convert(Nanoseconds, Milliseconds, dur.nanoseconds)).int
+      convert(Nanoseconds, Milliseconds, dur.nanosecond)).int
 else:
   proc getStartMilsecs*(): int {.deprecated, tags: [TimeEffect], benign.} =
     when defined(macosx):
diff --git a/lib/pure/unicode.nim b/lib/pure/unicode.nim
index 257c620f7..bfd01be55 100644
--- a/lib/pure/unicode.nim
+++ b/lib/pure/unicode.nim
@@ -9,7 +9,7 @@
 
 ## This module provides support to handle the Unicode UTF-8 encoding.
 
-{.deadCodeElim: on.}
+{.deadCodeElim: on.}  # dce option deprecated
 
 include "system/inclrtl"
 
@@ -1826,8 +1826,8 @@ when isMainModule:
   doAssert(runeSubStr(s, 17, 1) == "€")
   # echo runeStrAtPos(s, 18) # index error
 
-  doAssert(runeSubStr(s, 0) ==  "Hänsel  ««: 10,00€")
-  doAssert(runeSubStr(s, -18) ==  "Hänsel  ««: 10,00€")
+  doAssert(runeSubStr(s, 0) == "Hänsel  ««: 10,00€")
+  doAssert(runeSubStr(s, -18) == "Hänsel  ««: 10,00€")
   doAssert(runeSubStr(s, 10) == ": 10,00€")
   doAssert(runeSubStr(s, 18) == "")
   doAssert(runeSubStr(s, 0, 10) == "Hänsel  ««")
@@ -1840,7 +1840,7 @@ when isMainModule:
   doAssert(runeSubStr(s, -6, 5) == "10,00")
   doAssert(runeSubStr(s, -6, -1) == "10,00")
 
-  doAssert(runeSubStr(s, 0, 100) ==  "Hänsel  ««: 10,00€")
-  doAssert(runeSubStr(s, -100, 100) ==  "Hänsel  ««: 10,00€")
+  doAssert(runeSubStr(s, 0, 100) == "Hänsel  ««: 10,00€")
+  doAssert(runeSubStr(s, -100, 100) == "Hänsel  ««: 10,00€")
   doAssert(runeSubStr(s, 0, -100) == "")
   doAssert(runeSubStr(s, 100, -100) == "")
diff --git a/lib/pure/uri.nim b/lib/pure/uri.nim
index d2d11253a..dd8040928 100644
--- a/lib/pure/uri.nim
+++ b/lib/pure/uri.nim
@@ -18,14 +18,12 @@ type
     hostname*, port*, path*, query*, anchor*: string
     opaque*: bool
 
-{.deprecated: [TUrl: Url, TUri: Uri].}
-
 {.push warning[deprecated]: off.}
-proc `$`*(url: Url): string {.deprecated.} =
+proc `$`*(url: Url): string {.deprecated: "use Uri instead".} =
   ## **Deprecated since 0.9.6**: Use ``Uri`` instead.
   return string(url)
 
-proc `/`*(a, b: Url): Url {.deprecated.} =
+proc `/`*(a, b: Url): Url {.deprecated: "use Uri instead".} =
   ## Joins two URLs together, separating them with / if needed.
   ##
   ## **Deprecated since 0.9.6**: Use ``Uri`` instead.
@@ -40,39 +38,50 @@ proc `/`*(a, b: Url): Url {.deprecated.} =
     urlS.add(bs)
   result = Url(urlS)
 
-proc add*(url: var Url, a: Url) {.deprecated.} =
+proc add*(url: var Url, a: Url) {.deprecated: "use Uri instead".} =
   ## Appends url to url.
   ##
   ## **Deprecated since 0.9.6**: Use ``Uri`` instead.
   url = url / a
 {.pop.}
 
-proc encodeUrl*(s: string): string =
-  ## Encodes a value to be HTTP safe: This means that characters in the set
-  ## ``{'A'..'Z', 'a'..'z', '0'..'9', '_'}`` are carried over to the result,
-  ## a space is converted to ``'+'`` and every other character is encoded as
-  ## ``'%xx'`` where ``xx`` denotes its hexadecimal value.
+proc encodeUrl*(s: string, usePlus=true): string =
+  ## Encodes a URL according to RFC3986.
+  ##
+  ## This means that characters in the set
+  ## ``{'a'..'z', 'A'..'Z', '0'..'9', '-', '.', '_', '~'}`` are
+  ## carried over to the result.
+  ## All other characters are encoded as ``''%xx'`` where ``xx``
+  ## denotes its hexadecimal value.
+  ##
+  ## As a special rule, when the value of ``usePlus`` is true,
+  ## spaces are encoded as ``'+'`` instead of ``'%20'``.
   result = newStringOfCap(s.len + s.len shr 2) # assume 12% non-alnum-chars
-  for i in 0..s.len-1:
-    case s[i]
-    of 'a'..'z', 'A'..'Z', '0'..'9', '_': add(result, s[i])
-    of ' ': add(result, '+')
+  let fromSpace = if usePlus: "+" else: "%20"
+  for c in s:
+    case c
+    of 'a'..'z', 'A'..'Z', '0'..'9', '-', '.', '_', '~': add(result, c)
+    of ' ': add(result, fromSpace)
     else:
       add(result, '%')
-      add(result, toHex(ord(s[i]), 2))
-      
-proc decodeUrl*(s: string): string =
-  ## Decodes a value from its HTTP representation: This means that a ``'+'``
-  ## is converted to a space, ``'%xx'`` (where ``xx`` denotes a hexadecimal
-  ## value) is converted to the character with ordinal number ``xx``, and
+      add(result, toHex(ord(c), 2))
+
+proc decodeUrl*(s: string, decodePlus=true): string =
+  ## Decodes a URL according to RFC3986.
+  ##
+  ## This means that any ``'%xx'`` (where ``xx`` denotes a hexadecimal
+  ## value) are converted to the character with ordinal number ``xx``,
   ## and every other character is carried over.
+  ##
+  ## As a special rule, when the value of ``decodePlus`` is true, ``'+'``
+  ## characters are converted to a space.
   proc handleHexChar(c: char, x: var int) {.inline.} =
     case c
     of '0'..'9': x = (x shl 4) or (ord(c) - ord('0'))
     of 'a'..'f': x = (x shl 4) or (ord(c) - ord('a') + 10)
     of 'A'..'F': x = (x shl 4) or (ord(c) - ord('A') + 10)
     else: assert(false)
-    
+
   result = newString(s.len)
   var i = 0
   var j = 0
@@ -84,7 +93,11 @@ proc decodeUrl*(s: string): string =
       handleHexChar(s[i+2], x)
       inc(i, 2)
       result[j] = chr(x)
-    of '+': result[j] = ' '
+    of '+':
+      if decodePlus:
+        result[j] = ' '
+      else:
+        result[j] = s[i]
     else: result[j] = s[i]
     inc(i)
     inc(j)
@@ -94,7 +107,7 @@ proc parseAuthority(authority: string, result: var Uri) =
   var i = 0
   var inPort = false
   var inIPv6 = false
-  while true:
+  while i < authority.len:
     case authority[i]
     of '@':
       swap result.password, result.port
@@ -111,7 +124,6 @@ proc parseAuthority(authority: string, result: var Uri) =
       inIPv6 = true
     of ']':
       inIPv6 = false
-    of '\0': break
     else:
       if inPort:
         result.port.add(authority[i])
@@ -128,11 +140,11 @@ proc parsePath(uri: string, i: var int, result: var Uri) =
     parseAuthority(result.path, result)
     result.path.setLen(0)
 
-  if uri[i] == '?':
+  if i < uri.len and uri[i] == '?':
     i.inc # Skip '?'
     i.inc parseUntil(uri, result.query, {'#'}, i)
 
-  if uri[i] == '#':
+  if i < uri.len and uri[i] == '#':
     i.inc # Skip '#'
     i.inc parseUntil(uri, result.anchor, {}, i)
 
@@ -156,7 +168,7 @@ proc parseUri*(uri: string, result: var Uri) =
 
   # Check if this is a reference URI (relative URI)
   let doubleSlash = uri.len > 1 and uri[1] == '/'
-  if uri[i] == '/':
+  if i < uri.len and uri[i] == '/':
     # Make sure ``uri`` doesn't begin with '//'.
     if not doubleSlash:
       parsePath(uri, i, result)
@@ -164,7 +176,7 @@ proc parseUri*(uri: string, result: var Uri) =
 
   # Scheme
   i.inc parseWhile(uri, result.scheme, Letters + Digits + {'+', '-', '.'}, i)
-  if uri[i] != ':' and not doubleSlash:
+  if (i >= uri.len or uri[i] != ':') and not doubleSlash:
     # Assume this is a reference URI (relative URI)
     i = 0
     result.scheme.setLen(0)
@@ -174,7 +186,7 @@ proc parseUri*(uri: string, result: var Uri) =
     i.inc # Skip ':'
 
   # Authority
-  if uri[i] == '/' and uri[i+1] == '/':
+  if i+1 < uri.len and uri[i] == '/' and uri[i+1] == '/':
     i.inc(2) # Skip //
     var authority = ""
     i.inc parseUntil(uri, authority, {'/', '?', '#'}, i)
@@ -197,13 +209,13 @@ proc removeDotSegments(path: string): string =
   let endsWithSlash = path[path.len-1] == '/'
   var i = 0
   var currentSegment = ""
-  while true:
+  while i < path.len:
     case path[i]
     of '/':
       collection.add(currentSegment)
       currentSegment = ""
     of '.':
-      if path[i+1] == '.' and path[i+2] == '/':
+      if i+2 < path.len and path[i+1] == '.' and path[i+2] == '/':
         if collection.len > 0:
           discard collection.pop()
           i.inc 3
@@ -212,13 +224,11 @@ proc removeDotSegments(path: string): string =
         i.inc 2
         continue
       currentSegment.add path[i]
-    of '\0':
-      if currentSegment != "":
-        collection.add currentSegment
-      break
     else:
       currentSegment.add path[i]
     i.inc
+  if currentSegment != "":
+    collection.add currentSegment
 
   result = collection.join("/")
   if endsWithSlash: result.add '/'
@@ -320,18 +330,18 @@ proc `/`*(x: Uri, path: string): Uri =
   result = x
 
   if result.path.len == 0:
-    if path[0] != '/':
+    if path.len == 0 or path[0] != '/':
       result.path = "/"
     result.path.add(path)
     return
 
-  if result.path[result.path.len-1] == '/':
-    if path[0] == '/':
+  if result.path.len > 0 and result.path[result.path.len-1] == '/':
+    if path.len > 0 and path[0] == '/':
       result.path.add(path[1 .. path.len-1])
     else:
       result.path.add(path)
   else:
-    if path[0] != '/':
+    if path.len == 0 or path[0] != '/':
       result.path.add '/'
     result.path.add(path)
 
@@ -373,7 +383,10 @@ when isMainModule:
     const test1 = "abc\L+def xyz"
     doAssert encodeUrl(test1) == "abc%0A%2Bdef+xyz"
     doAssert decodeUrl(encodeUrl(test1)) == test1
-    
+    doAssert encodeUrl(test1, false) == "abc%0A%2Bdef%20xyz"
+    doAssert decodeUrl(encodeUrl(test1, false), false) == test1
+    doAssert decodeUrl(encodeUrl(test1)) == test1
+
   block:
     let str = "http://localhost"
     let test = parseUri(str)
@@ -464,7 +477,7 @@ when isMainModule:
     doAssert test.hostname == "github.com"
     doAssert test.port == "dom96"
     doAssert test.path == "/packages"
-    
+
   block:
     let str = "file:///foo/bar/baz.txt"
     let test = parseUri(str)
diff --git a/lib/pure/xmldom.nim b/lib/pure/xmldom.nim
index 3c891c81b..8cd47aa39 100644
--- a/lib/pure/xmldom.nim
+++ b/lib/pure/xmldom.nim
@@ -232,10 +232,10 @@ proc createAttributeNS*(doc: PDocument, namespaceURI: string, qualifiedName: str
     raise newException(EInvalidCharacterErr, "Invalid character")
   # Exceptions
   if qualifiedName.contains(':'):
-    let qfnamespaces = qualifiedName.toLower().split(':')
+    let qfnamespaces = qualifiedName.toLowerAscii().split(':')
     if isNil(namespaceURI):
       raise newException(ENamespaceErr, "When qualifiedName contains a prefix namespaceURI cannot be nil")
-    elif qfnamespaces[0] == "xml" and 
+    elif qfnamespaces[0] == "xml" and
         namespaceURI != "http://www.w3.org/XML/1998/namespace" and
         qfnamespaces[1] notin stdattrnames:
       raise newException(ENamespaceErr,
@@ -311,10 +311,10 @@ proc createElement*(doc: PDocument, tagName: string): PElement =
 proc createElementNS*(doc: PDocument, namespaceURI: string, qualifiedName: string): PElement =
   ## Creates an element of the given qualified name and namespace URI.
   if qualifiedName.contains(':'):
-    let qfnamespaces = qualifiedName.toLower().split(':')
+    let qfnamespaces = qualifiedName.toLowerAscii().split(':')
     if isNil(namespaceURI):
       raise newException(ENamespaceErr, "When qualifiedName contains a prefix namespaceURI cannot be nil")
-    elif qfnamespaces[0] == "xml" and 
+    elif qfnamespaces[0] == "xml" and
         namespaceURI != "http://www.w3.org/XML/1998/namespace" and
         qfnamespaces[1] notin stdattrnames:
       raise newException(ENamespaceErr,
@@ -533,13 +533,13 @@ proc `prefix=`*(n: PNode, value: string) =
 
   if isNil(n.fNamespaceURI):
     raise newException(ENamespaceErr, "namespaceURI cannot be nil")
-  elif value.toLower() == "xml" and n.fNamespaceURI != "http://www.w3.org/XML/1998/namespace":
+  elif value.toLowerAscii() == "xml" and n.fNamespaceURI != "http://www.w3.org/XML/1998/namespace":
     raise newException(ENamespaceErr,
       "When the namespace prefix is \"xml\" namespaceURI has to be \"http://www.w3.org/XML/1998/namespace\"")
-  elif value.toLower() == "xmlns" and n.fNamespaceURI != "http://www.w3.org/2000/xmlns/":
+  elif value.toLowerAscii() == "xmlns" and n.fNamespaceURI != "http://www.w3.org/2000/xmlns/":
     raise newException(ENamespaceErr,
       "When the namespace prefix is \"xmlns\" namespaceURI has to be \"http://www.w3.org/2000/xmlns/\"")
-  elif value.toLower() == "xmlns" and n.fNodeType == AttributeNode:
+  elif value.toLowerAscii() == "xmlns" and n.fNodeType == AttributeNode:
     raise newException(ENamespaceErr, "An AttributeNode cannot have a prefix of \"xmlns\"")
 
   n.fNodeName = value & ":" & n.fLocalName
@@ -1069,17 +1069,15 @@ proc splitData*(textNode: PText, offset: int): PText =
     var newNode: PText = textNode.fOwnerDocument.createTextNode(right)
     return newNode
 
-
 # ProcessingInstruction
 proc target*(pi: PProcessingInstruction): string =
   ## Returns the Processing Instructions target
 
   return pi.fTarget
 
-
-# --Other stuff--
-# Writer
-proc addEscaped(s: string): string =
+proc escapeXml*(s: string; result: var string) =
+  ## Prepares a string for insertion into a XML document
+  ## by escaping the XML special characters.
   result = ""
   for c in items(s):
     case c
@@ -1089,11 +1087,20 @@ proc addEscaped(s: string): string =
     of '"': result.add("&quot;")
     else: result.add(c)
 
+proc escapeXml*(s: string): string =
+  ## Prepares a string for insertion into a XML document
+  ## by escaping the XML special characters.
+  result = newStringOfCap(s.len + s.len shr 4)
+  escapeXml(s, result)
+
+# --Other stuff--
+# Writer
+
 proc nodeToXml(n: PNode, indent: int = 0): string =
   result = spaces(indent) & "<" & n.nodeName
   if not isNil(n.attributes):
     for i in items(n.attributes):
-      result.add(" " & i.name & "=\"" & addEscaped(i.value) & "\"")
+      result.add(" " & i.name & "=\"" & escapeXml(i.value) & "\"")
 
   if isNil(n.childNodes) or n.childNodes.len() == 0:
     result.add("/>") # No idea why this doesn't need a \n :O
@@ -1106,7 +1113,7 @@ proc nodeToXml(n: PNode, indent: int = 0): string =
         result.add(nodeToXml(i, indent + 2))
       of TextNode:
         result.add(spaces(indent * 2))
-        result.add(addEscaped(i.nodeValue))
+        result.add(escapeXml(i.nodeValue))
       of CDataSectionNode:
         result.add(spaces(indent * 2))
         result.add("<![CDATA[" & i.nodeValue & "]]>")
diff --git a/lib/pure/xmlparser.nim b/lib/pure/xmlparser.nim
index 9159b4bfc..597b80eb5 100644
--- a/lib/pure/xmlparser.nim
+++ b/lib/pure/xmlparser.nim
@@ -16,8 +16,6 @@ type
                                    ## for invalid XML.
     errors*: seq[string]           ## All detected parsing errors.
 
-{.deprecated: [EInvalidXml: XmlError].}
-
 proc raiseInvalidXml(errors: seq[string]) =
   var e: ref XmlError
   new(e)
diff --git a/lib/pure/xmltree.nim b/lib/pure/xmltree.nim
index 45696c80c..8f85cb5c9 100644
--- a/lib/pure/xmltree.nim
+++ b/lib/pure/xmltree.nim
@@ -12,9 +12,9 @@
 import macros, strtabs
 
 type
-  XmlNode* = ref XmlNodeObj ## an XML tree consists of ``PXmlNode``'s.
+  XmlNode* = ref XmlNodeObj ## an XML tree consists of ``XmlNode``'s.
 
-  XmlNodeKind* = enum  ## different kinds of ``PXmlNode``'s
+  XmlNodeKind* = enum  ## different kinds of ``XmlNode``'s
     xnText,             ## a text element
     xnElement,          ## an element with 0 or more children
     xnCData,            ## a CDATA node
@@ -33,9 +33,6 @@ type
       fAttr: XmlAttributes
     fClientData: int              ## for other clients
 
-{.deprecated: [PXmlNode: XmlNode, TXmlNodeKind: XmlNodeKind, PXmlAttributes:
-    XmlAttributes, TXmlNode: XmlNodeObj].}
-
 proc newXmlNode(kind: XmlNodeKind): XmlNode =
   ## creates a new ``XmlNode``.
   new(result)
@@ -155,11 +152,6 @@ proc `[]`* (n: var XmlNode, i: int): var XmlNode {.inline.} =
   assert n.k == xnElement
   result = n.s[i]
 
-proc mget*(n: var XmlNode, i: int): var XmlNode {.inline, deprecated.} =
-  ## returns the `i`'th child of `n` so that it can be modified. Use ```[]```
-  ## instead.
-  n[i]
-
 iterator items*(n: XmlNode): XmlNode {.inline.} =
   ## iterates over any child of `n`.
   assert n.k == xnElement
@@ -315,14 +307,12 @@ proc newXmlTree*(tag: string, children: openArray[XmlNode],
   for i in 0..children.len-1: result.s[i] = children[i]
   result.fAttr = attributes
 
-proc xmlConstructor(e: NimNode): NimNode {.compileTime.} =
-  expectLen(e, 2)
-  var a = e[1]
+proc xmlConstructor(a: NimNode): NimNode {.compileTime.} =
   if a.kind == nnkCall:
     result = newCall("newXmlTree", toStrLit(a[0]))
     var attrs = newNimNode(nnkBracket, a)
-    var newStringTabCall = newCall("newStringTable", attrs,
-                                   newIdentNode("modeCaseSensitive"))
+    var newStringTabCall = newCall(bindSym"newStringTable", attrs,
+                                    bindSym"modeCaseSensitive")
     var elements = newNimNode(nnkBracket, a)
     for i in 1..a.len-1:
       if a[i].kind == nnkExprEqExpr:
@@ -348,7 +338,6 @@ macro `<>`*(x: untyped): untyped =
   ##
   ##  <a href="http://nim-lang.org">Nim rules.</a>
   ##
-  let x = callsite()
   result = xmlConstructor(x)
 
 proc child*(n: XmlNode, name: string): XmlNode =
diff --git a/lib/system.nim b/lib/system.nim
index 3761a35f8..5781aff27 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -146,7 +146,7 @@ proc declared*(x: untyped): bool {.magic: "Defined", noSideEffect, compileTime.}
   ##     # missing it.
 
 when defined(useNimRtl):
-  {.deadCodeElim: on.}
+  {.deadCodeElim: on.}  # dce option deprecated
 
 proc definedInScope*(x: untyped): bool {.
   magic: "DefinedInScope", noSideEffect, deprecated, compileTime.}
@@ -1280,15 +1280,13 @@ proc setLen*[T](s: var seq[T], newlen: Natural) {.
   ## sets the length of `s` to `newlen`.
   ## ``T`` may be any sequence type.
   ## If the current length is greater than the new length,
-  ## ``s`` will be truncated. `s` cannot be nil! To initialize a sequence with
-  ## a size, use ``newSeq`` instead.
+  ## ``s`` will be truncated.
 
 proc setLen*(s: var string, newlen: Natural) {.
   magic: "SetLengthStr", noSideEffect.}
   ## sets the length of `s` to `newlen`.
   ## If the current length is greater than the new length,
-  ## ``s`` will be truncated. `s` cannot be nil! To initialize a string with
-  ## a size, use ``newString`` instead.
+  ## ``s`` will be truncated.
   ##
   ## .. code-block:: Nim
   ##  var myS = "Nim is great!!"
@@ -2404,7 +2402,7 @@ proc `==` *[T](x, y: seq[T]): bool {.noSideEffect.} =
     if x.isNil and y.isNil:
       return true
   else:
-    when not defined(JS) or defined(nimphp):
+    when not defined(JS):
       proc seqToPtr[T](x: seq[T]): pointer {.inline, nosideeffect.} =
         result = cast[pointer](x)
     else:
@@ -2414,8 +2412,9 @@ proc `==` *[T](x, y: seq[T]): bool {.noSideEffect.} =
     if seqToPtr(x) == seqToPtr(y):
       return true
 
-  if x.isNil or y.isNil:
-    return false
+  when not defined(nimNoNil):
+    if x.isNil or y.isNil:
+      return false
 
   if x.len != y.len:
     return false
@@ -2766,17 +2765,14 @@ type
 
 when defined(JS):
   proc add*(x: var string, y: cstring) {.asmNoStackFrame.} =
-    when defined(nimphp):
-      asm """`x` .= `y`;"""
-    else:
-      asm """
-        var len = `x`[0].length-1;
-        for (var i = 0; i < `y`.length; ++i) {
-          `x`[0][len] = `y`.charCodeAt(i);
-          ++len;
-        }
-        `x`[0][len] = 0
-      """
+    asm """
+      var len = `x`[0].length-1;
+      for (var i = 0; i < `y`.length; ++i) {
+        `x`[0][len] = `y`.charCodeAt(i);
+        ++len;
+      }
+      `x`[0][len] = 0
+    """
   proc add*(x: var cstring, y: cstring) {.magic: "AppendStrStr".}
 
 elif hasAlloc:
@@ -3227,7 +3223,7 @@ when not defined(JS): #and not defined(nimscript):
     when declared(initGC): initGC()
 
   when not defined(nimscript):
-    proc setControlCHook*(hook: proc () {.noconv.} not nil)
+    proc setControlCHook*(hook: proc () {.noconv.})
       ## allows you to override the behaviour of your application when CTRL+C
       ## is pressed. Only one such hook is supported.
 
@@ -3936,29 +3932,48 @@ proc addEscapedChar*(s: var string, c: char) {.noSideEffect, inline.} =
   ## * replaces any ``\`` by ``\\``
   ## * replaces any ``'`` by ``\'``
   ## * replaces any ``"`` by ``\"``
-  ## * replaces any other character in the set ``{'\0'..'\31', '\127'..'\255'}``
+  ## * replaces any ``\a`` by ``\\a``
+  ## * replaces any ``\b`` by ``\\b``
+  ## * replaces any ``\t`` by ``\\t``
+  ## * replaces any ``\n`` by ``\\n``
+  ## * replaces any ``\v`` by ``\\v``
+  ## * replaces any ``\f`` by ``\\f``
+  ## * replaces any ``\c`` by ``\\c``
+  ## * replaces any ``\e`` by ``\\e``
+  ## * replaces any other character not in the set ``{'\21..'\126'}
   ##   by ``\xHH`` where ``HH`` is its hexadecimal value.
   ##
   ## The procedure has been designed so that its output is usable for many
   ## different common syntaxes.
   ## **Note**: This is not correct for producing Ansi C code!
   case c
-  of '\0'..'\31', '\127'..'\255':
-    add(s, "\\x")
+  of '\a': s.add "\\a" # \x07
+  of '\b': s.add "\\b" # \x08
+  of '\t': s.add "\\t" # \x09
+  of '\n': s.add "\\n" # \x0A
+  of '\v': s.add "\\v" # \x0B
+  of '\f': s.add "\\f" # \x0C
+  of '\c': s.add "\\c" # \x0D
+  of '\e': s.add "\\e" # \x1B
+  of '\\': s.add("\\\\")
+  of '\'': s.add("\\'")
+  of '\"': s.add("\\\"")
+  of {'\32'..'\126'} - {'\\', '\'', '\"'}: s.add(c)
+  else:
+    s.add("\\x")
     const HexChars = "0123456789ABCDEF"
     let n = ord(c)
     s.add(HexChars[int((n and 0xF0) shr 4)])
     s.add(HexChars[int(n and 0xF)])
-  of '\\': add(s, "\\\\")
-  of '\'': add(s, "\\'")
-  of '\"': add(s, "\\\"")
-  else: add(s, c)
 
 proc addQuoted*[T](s: var string, x: T) =
   ## Appends `x` to string `s` in place, applying quoting and escaping
   ## if `x` is a string or char. See
   ## `addEscapedChar <system.html#addEscapedChar>`_
-  ## for the escaping scheme.
+  ## for the escaping scheme. When `x` is a string, characters in the
+  ## range ``{\128..\255}`` are never escaped so that multibyte UTF-8
+  ## characters are untouched (note that this behavior is different from
+  ## ``addEscapedChar``).
   ##
   ## The Nim standard library uses this function on the elements of
   ## collections when producing a string representation of a collection.
@@ -3977,7 +3992,12 @@ proc addQuoted*[T](s: var string, x: T) =
   when T is string:
     s.add("\"")
     for c in x:
-      s.addEscapedChar(c)
+      # Only ASCII chars are escaped to avoid butchering
+      # multibyte UTF-8 characters.
+      if c <= 127.char:
+        s.addEscapedChar(c)
+      else:
+        s.add c
     s.add("\"")
   elif T is char:
     s.add("'")
@@ -3991,18 +4011,18 @@ proc addQuoted*[T](s: var string, x: T) =
 
 when hasAlloc:
   # XXX: make these the default (or implement the NilObject optimization)
-  proc safeAdd*[T](x: var seq[T], y: T) {.noSideEffect.} =
+  proc safeAdd*[T](x: var seq[T], y: T) {.noSideEffect, deprecated.} =
     ## Adds ``y`` to ``x`` unless ``x`` is not yet initialized; in that case,
     ## ``x`` becomes ``@[y]``
     if x == nil: x = @[y]
     else: x.add(y)
 
-  proc safeAdd*(x: var string, y: char) =
+  proc safeAdd*(x: var string, y: char) {.noSideEffect, deprecated.} =
     ## Adds ``y`` to ``x``. If ``x`` is ``nil`` it is initialized to ``""``
     if x == nil: x = ""
     x.add(y)
 
-  proc safeAdd*(x: var string, y: string) =
+  proc safeAdd*(x: var string, y: string) {.noSideEffect, deprecated.} =
     ## Adds ``y`` to ``x`` unless ``x`` is not yet initalized; in that
     ## case, ``x`` becomes ``y``
     if x == nil: x = y
@@ -4139,20 +4159,22 @@ template doAssertRaises*(exception, code: untyped): typed =
   runnableExamples:
     doAssertRaises(ValueError):
       raise newException(ValueError, "Hello World")
-
+  var wrong = false
   try:
-    block:
-      code
-    raiseAssert(astToStr(exception) & " wasn't raised by:\n" & astToStr(code))
+    code
+    wrong = true
   except exception:
     discard
   except Exception as exc:
     raiseAssert(astToStr(exception) &
                 " wasn't raised, another error was raised instead by:\n"&
                 astToStr(code))
+  if wrong:
+    raiseAssert(astToStr(exception) & " wasn't raised by:\n" & astToStr(code))
 
-when defined(cpp) and appType != "lib" and not defined(js) and
-    not defined(nimscript) and hostOS != "standalone":
+when defined(cpp) and appType != "lib" and
+    not defined(js) and not defined(nimscript) and
+    hostOS != "standalone" and not defined(noCppExceptions):
   proc setTerminate(handler: proc() {.noconv.})
     {.importc: "std::set_terminate", header: "<exception>".}
   setTerminate proc() {.noconv.} =
diff --git a/lib/system/assign.nim b/lib/system/assign.nim
index f061c89cf..16b56aba7 100644
--- a/lib/system/assign.nim
+++ b/lib/system/assign.nim
@@ -74,13 +74,17 @@ proc genericAssignAux(dest, src: pointer, mt: PNimType, shallow: bool) =
       var dst = cast[ByteAddress](cast[PPointer](dest)[])
       for i in 0..seq.len-1:
         genericAssignAux(
-          cast[pointer](dst +% i*% mt.base.size +% GenericSeqSize),
+          cast[pointer](dst +% i *% mt.base.size +% GenericSeqSize),
           cast[pointer](cast[ByteAddress](s2) +% i *% mt.base.size +%
                       GenericSeqSize),
           mt.base, shallow)
   of tyObject:
-    if mt.base != nil:
-      genericAssignAux(dest, src, mt.base, shallow)
+    var it = mt.base
+    # don't use recursion here on the PNimType because the subtype
+    # check should only be done at the very end:
+    while it != nil:
+      genericAssignAux(dest, src, it.node, shallow)
+      it = it.base
     genericAssignAux(dest, src, mt.node, shallow)
     # we need to copy m_type field for tyObject, as it could be empty for
     # sequence reallocations:
@@ -89,13 +93,15 @@ proc genericAssignAux(dest, src: pointer, mt: PNimType, shallow: bool) =
     #   if p of TB:
     #     var tbObj = TB(p)
     #     tbObj of TC # needs to be false!
+    #c_fprintf(stdout, "%s %s\n", pint[].name, mt.name)
+    chckObjAsgn(cast[ptr PNimType](src)[], mt)
     pint[] = mt # cast[ptr PNimType](src)[]
   of tyTuple:
     genericAssignAux(dest, src, mt.node, shallow)
   of tyArray, tyArrayConstr:
     for i in 0..(mt.size div mt.base.size)-1:
-      genericAssignAux(cast[pointer](d +% i*% mt.base.size),
-                       cast[pointer](s +% i*% mt.base.size), mt.base, shallow)
+      genericAssignAux(cast[pointer](d +% i *% mt.base.size),
+                       cast[pointer](s +% i *% mt.base.size), mt.base, shallow)
   of tyRef:
     unsureAsgnRef(cast[PPointer](dest), cast[PPointer](s)[])
   of tyOptAsRef:
@@ -160,8 +166,8 @@ proc genericAssignOpenArray(dest, src: pointer, len: int,
     d = cast[ByteAddress](dest)
     s = cast[ByteAddress](src)
   for i in 0..len-1:
-    genericAssign(cast[pointer](d +% i*% mt.base.size),
-                  cast[pointer](s +% i*% mt.base.size), mt.base)
+    genericAssign(cast[pointer](d +% i *% mt.base.size),
+                  cast[pointer](s +% i *% mt.base.size), mt.base)
 
 proc objectInit(dest: pointer, typ: PNimType) {.compilerProc, benign.}
 proc objectInitAux(dest: pointer, n: ptr TNimNode) {.benign.} =
@@ -229,7 +235,7 @@ proc genericReset(dest: pointer, mt: PNimType) =
     pint[] = nil
   of tyArray, tyArrayConstr:
     for i in 0..(mt.size div mt.base.size)-1:
-      genericReset(cast[pointer](d +% i*% mt.base.size), mt.base)
+      genericReset(cast[pointer](d +% i *% mt.base.size), mt.base)
   else:
     zeroMem(dest, mt.size) # set raw bits to zero
 
diff --git a/lib/system/atomics.nim b/lib/system/atomics.nim
index fa3700190..56ebde823 100644
--- a/lib/system/atomics.nim
+++ b/lib/system/atomics.nim
@@ -241,7 +241,7 @@ when defined(vcc):
     else:
       {.error: "invalid CAS instruction".}
 
-elif defined(tcc) and not defined(windows):
+elif defined(tcc):
   when defined(amd64):
     {.emit:"""
 static int __tcc_cas(int *ptr, int oldVal, int newVal)
@@ -262,7 +262,7 @@ static int __tcc_cas(int *ptr, int oldVal, int newVal)
 }
 """.}
   else:
-    assert sizeof(int) == 4
+    #assert sizeof(int) == 4
     {.emit:"""
 static int __tcc_cas(int *ptr, int oldVal, int newVal)
 {
diff --git a/lib/system/deepcopy.nim b/lib/system/deepcopy.nim
index 51e138e5e..750da00cf 100644
--- a/lib/system/deepcopy.nim
+++ b/lib/system/deepcopy.nim
@@ -105,7 +105,7 @@ proc genericDeepCopyAux(dest, src: pointer, mt: PNimType; tab: var PtrTable) =
     var dst = cast[ByteAddress](cast[PPointer](dest)[])
     for i in 0..seq.len-1:
       genericDeepCopyAux(
-        cast[pointer](dst +% i*% mt.base.size +% GenericSeqSize),
+        cast[pointer](dst +% i *% mt.base.size +% GenericSeqSize),
         cast[pointer](cast[ByteAddress](s2) +% i *% mt.base.size +%
                      GenericSeqSize),
         mt.base, tab)
@@ -122,8 +122,8 @@ proc genericDeepCopyAux(dest, src: pointer, mt: PNimType; tab: var PtrTable) =
     genericDeepCopyAux(dest, src, mt.node, tab)
   of tyArray, tyArrayConstr:
     for i in 0..(mt.size div mt.base.size)-1:
-      genericDeepCopyAux(cast[pointer](d +% i*% mt.base.size),
-                         cast[pointer](s +% i*% mt.base.size), mt.base, tab)
+      genericDeepCopyAux(cast[pointer](d +% i *% mt.base.size),
+                         cast[pointer](s +% i *% mt.base.size), mt.base, tab)
   of tyRef, tyOptAsRef:
     let s2 = cast[PPointer](src)[]
     if s2 == nil:
@@ -183,5 +183,5 @@ proc genericDeepCopyOpenArray(dest, src: pointer, len: int,
     d = cast[ByteAddress](dest)
     s = cast[ByteAddress](src)
   for i in 0..len-1:
-    genericDeepCopy(cast[pointer](d +% i*% mt.base.size),
-                    cast[pointer](s +% i*% mt.base.size), mt.base)
+    genericDeepCopy(cast[pointer](d +% i *% mt.base.size),
+                    cast[pointer](s +% i *% mt.base.size), mt.base)
diff --git a/lib/system/embedded.nim b/lib/system/embedded.nim
index a14f43e7e..46e84e056 100644
--- a/lib/system/embedded.nim
+++ b/lib/system/embedded.nim
@@ -40,4 +40,4 @@ proc reraiseException() {.compilerRtl.} =
 
 proc writeStackTrace() = discard
 
-proc setControlCHook(hook: proc () {.noconv.} not nil) = discard
+proc setControlCHook(hook: proc () {.noconv.}) = discard
diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim
index afeab2b6c..fb38948f7 100644
--- a/lib/system/excpt.nim
+++ b/lib/system/excpt.nim
@@ -56,7 +56,7 @@ var
     # list of exception handlers
     # a global variable for the root of all try blocks
   currException {.threadvar.}: ref Exception
-  raise_counter {.threadvar.}: uint 
+  raise_counter {.threadvar.}: uint
 
   gcFramePtr {.threadvar.}: GcFrame
 
@@ -126,10 +126,10 @@ proc popCurrentExceptionEx(id: uint) {.compilerRtl.} =
     while cur != nil and cur.raise_id != id:
       prev = cur
       cur = cur.up
-    if cur == nil: 
+    if cur == nil:
       showErrorMessage("popCurrentExceptionEx() exception was not found in the exception stack. Aborting...")
       quitOrDebug()
-    prev.up = cur.up  
+    prev.up = cur.up
 
 # some platforms have native support for stack traces:
 const
@@ -503,7 +503,7 @@ when not defined(noSignalHandler) and not defined(useNimRtl):
 
   registerSignalHandler() # call it in initialization section
 
-proc setControlCHook(hook: proc () {.noconv.} not nil) =
+proc setControlCHook(hook: proc () {.noconv.}) =
   # ugly cast, but should work on all architectures:
   type SignalHandler = proc (sign: cint) {.noconv, benign.}
   c_signal(SIGINT, cast[SignalHandler](hook))
diff --git a/lib/system/gc.nim b/lib/system/gc.nim
index 66d49ce1b..425963f3f 100644
--- a/lib/system/gc.nim
+++ b/lib/system/gc.nim
@@ -535,7 +535,7 @@ proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer =
 
   var oldsize = cast[PGenericSeq](old).len*elemSize + GenericSeqSize
   copyMem(res, ol, oldsize + sizeof(Cell))
-  zeroMem(cast[pointer](cast[ByteAddress](res)+% oldsize +% sizeof(Cell)),
+  zeroMem(cast[pointer](cast[ByteAddress](res) +% oldsize +% sizeof(Cell)),
           newsize-oldsize)
   sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "growObj: 3")
   # This can be wrong for intermediate temps that are nevertheless on the
diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim
index 8c3175eac..56a9f6575 100644
--- a/lib/system/jssys.nim
+++ b/lib/system/jssys.nim
@@ -48,10 +48,7 @@ proc nimCharToStr(x: char): string {.compilerproc.} =
   result[0] = x
 
 proc isNimException(): bool {.asmNoStackFrame.} =
-  when defined(nimphp):
-    asm "return isset(`lastJSError`['m_type']);"
-  else:
-    asm "return `lastJSError`.m_type;"
+  asm "return `lastJSError`.m_type;"
 
 proc getCurrentException*(): ref Exception {.compilerRtl, benign.} =
   if isNimException(): result = cast[ref Exception](lastJSError)
@@ -61,15 +58,14 @@ proc getCurrentExceptionMsg*(): string =
     if isNimException():
       return cast[Exception](lastJSError).msg
     else:
-      when not defined(nimphp):
-        var msg: cstring
-        {.emit: """
-        if (`lastJSError`.message !== undefined) {
-          `msg` = `lastJSError`.message;
-        }
-        """.}
-        if not msg.isNil:
-          return $msg
+      var msg: cstring
+      {.emit: """
+      if (`lastJSError`.message !== undefined) {
+        `msg` = `lastJSError`.message;
+      }
+      """.}
+      if not msg.isNil:
+        return $msg
   return ""
 
 proc auxWriteStackTrace(f: PCallFrame): string =
@@ -140,12 +136,9 @@ proc raiseException(e: ref Exception, ename: cstring) {.
   e.name = ename
   if excHandler == 0:
     unhandledException(e)
-  when defined(nimphp):
-    asm """throw new Exception($`e`["message"]);"""
-  else:
-    when NimStackTrace:
-      e.trace = rawWriteStackTrace()
-    asm "throw `e`;"
+  when NimStackTrace:
+    e.trace = rawWriteStackTrace()
+  asm "throw `e`;"
 
 proc reraiseException() {.compilerproc, asmNoStackFrame.} =
   if lastJSError == nil:
@@ -173,57 +166,35 @@ proc raiseFieldError(f: string) {.compilerproc, noreturn.} =
   raise newException(FieldError, f & " is not accessible")
 
 proc setConstr() {.varargs, asmNoStackFrame, compilerproc.} =
-  when defined(nimphp):
-    asm """
-      $args = func_get_args();
-      $result = array();
-      foreach ($args as $x) {
-        if (is_array($x)) {
-          for ($j = $x[0]; $j <= $x[1]; $j++) {
-            $result[$j] = true;
-          }
-        } else {
-          $result[$x] = true;
-        }
-      }
-      return $result;
-    """
-  else:
-    asm """
-      var result = {};
-      for (var i = 0; i < arguments.length; ++i) {
-        var x = arguments[i];
-        if (typeof(x) == "object") {
-          for (var j = x[0]; j <= x[1]; ++j) {
-            result[j] = true;
-          }
-        } else {
-          result[x] = true;
+  asm """
+    var result = {};
+    for (var i = 0; i < arguments.length; ++i) {
+      var x = arguments[i];
+      if (typeof(x) == "object") {
+        for (var j = x[0]; j <= x[1]; ++j) {
+          result[j] = true;
         }
+      } else {
+        result[x] = true;
       }
-      return result;
-    """
-
-proc makeNimstrLit(c: cstring): string {.asmNoStackFrame, compilerproc.} =
-  when defined(nimphp):
-    {.emit: """return `c`;""".}
-  else:
-    {.emit: """
-    var ln = `c`.length;
-    var result = new Array(ln + 1);
-    var i = 0;
-    for (; i < ln; ++i) {
-      result[i] = `c`.charCodeAt(i);
     }
-    result[i] = 0; // terminating zero
     return result;
-    """.}
+  """
+
+proc makeNimstrLit(c: cstring): string {.asmNoStackFrame, compilerproc.} =
+  {.emit: """
+  var ln = `c`.length;
+  var result = new Array(ln + 1);
+  var i = 0;
+  for (; i < ln; ++i) {
+    result[i] = `c`.charCodeAt(i);
+  }
+  result[i] = 0; // terminating zero
+  return result;
+  """.}
 
 proc cstrToNimstr(c: cstring): string {.asmNoStackFrame, compilerproc.} =
-  when defined(nimphp):
-    {.emit: """return `c`;""".}
-  else:
-    {.emit: """
+  {.emit: """
   var ln = `c`.length;
   var result = new Array(ln);
   var r = 0;
@@ -261,156 +232,93 @@ proc cstrToNimstr(c: cstring): string {.asmNoStackFrame, compilerproc.} =
   """.}
 
 proc toJSStr(s: string): cstring {.asmNoStackFrame, compilerproc.} =
-  when defined(nimphp):
-    {.emit: """return `s`;""".}
-  else:
-    asm """
-    var len = `s`.length-1;
-    var asciiPart = new Array(len);
-    var fcc = String.fromCharCode;
-    var nonAsciiPart = null;
-    var nonAsciiOffset = 0;
-    for (var i = 0; i < len; ++i) {
-      if (nonAsciiPart !== null) {
-        var offset = (i - nonAsciiOffset) * 2;
-        var code = `s`[i].toString(16);
-        if (code.length == 1) {
-          code = "0"+code;
-        }
-        nonAsciiPart[offset] = "%";
-        nonAsciiPart[offset + 1] = code;
-      }
-      else if (`s`[i] < 128)
-        asciiPart[i] = fcc(`s`[i]);
-      else {
-        asciiPart.length = i;
-        nonAsciiOffset = i;
-        nonAsciiPart = new Array((len - i) * 2);
-        --i;
+  asm """
+  var len = `s`.length-1;
+  var asciiPart = new Array(len);
+  var fcc = String.fromCharCode;
+  var nonAsciiPart = null;
+  var nonAsciiOffset = 0;
+  for (var i = 0; i < len; ++i) {
+    if (nonAsciiPart !== null) {
+      var offset = (i - nonAsciiOffset) * 2;
+      var code = `s`[i].toString(16);
+      if (code.length == 1) {
+        code = "0"+code;
       }
+      nonAsciiPart[offset] = "%";
+      nonAsciiPart[offset + 1] = code;
     }
-    asciiPart = asciiPart.join("");
-    return (nonAsciiPart === null) ?
-        asciiPart : asciiPart + decodeURIComponent(nonAsciiPart.join(""));
+    else if (`s`[i] < 128)
+      asciiPart[i] = fcc(`s`[i]);
+    else {
+      asciiPart.length = i;
+      nonAsciiOffset = i;
+      nonAsciiPart = new Array((len - i) * 2);
+      --i;
+    }
+  }
+  asciiPart = asciiPart.join("");
+  return (nonAsciiPart === null) ?
+      asciiPart : asciiPart + decodeURIComponent(nonAsciiPart.join(""));
   """
 
 proc mnewString(len: int): string {.asmNoStackFrame, compilerproc.} =
-  when defined(nimphp):
-    asm """
-      return str_repeat(chr(0),`len`);
-    """
-  else:
-    asm """
-      var result = new Array(`len`+1);
-      result[0] = 0;
-      result[`len`] = 0;
-      return result;
-    """
-
-when defined(nimphp):
-  proc nimAt(x: string; i: int): string {.asmNoStackFrame, compilerproc.} =
-    asm """
-      return `x`[`i`];
-    """
-
-when defined(nimphp):
-  proc nimSubstr(s: string; a, b: int): string {.
-      asmNoStackFrame, compilerproc.} =
-    asm """return substr(`s`,`a`,`b`-`a`+1);"""
+  asm """
+    var result = new Array(`len`+1);
+    result[0] = 0;
+    result[`len`] = 0;
+    return result;
+  """
 
 proc SetCard(a: int): int {.compilerproc, asmNoStackFrame.} =
   # argument type is a fake
-  when defined(nimphp):
-    asm """
-      return count(`a`);
-    """
-  else:
-    asm """
-      var result = 0;
-      for (var elem in `a`) { ++result; }
-      return result;
-    """
+  asm """
+    var result = 0;
+    for (var elem in `a`) { ++result; }
+    return result;
+  """
 
 proc SetEq(a, b: int): bool {.compilerproc, asmNoStackFrame.} =
-  when defined(nimphp):
-    asm """
-      foreach (`a` as $elem=>$_) { if (!isset(`b`[$elem])) return false; }
-      foreach (`b` as $elem=>$_) { if (!isset(`a`[$elem])) return false; }
-      return true;
-    """
-  else:
-    asm """
-      for (var elem in `a`) { if (!`b`[elem]) return false; }
-      for (var elem in `b`) { if (!`a`[elem]) return false; }
-      return true;
-    """
+  asm """
+    for (var elem in `a`) { if (!`b`[elem]) return false; }
+    for (var elem in `b`) { if (!`a`[elem]) return false; }
+    return true;
+  """
 
 proc SetLe(a, b: int): bool {.compilerproc, asmNoStackFrame.} =
-  when defined(nimphp):
-    asm """
-      foreach (`a` as $elem=>$_) { if (!isset(`b`[$elem])) return false; }
-      return true;
-    """
-  else:
-    asm """
-      for (var elem in `a`) { if (!`b`[elem]) return false; }
-      return true;
-    """
+  asm """
+    for (var elem in `a`) { if (!`b`[elem]) return false; }
+    return true;
+  """
 
 proc SetLt(a, b: int): bool {.compilerproc.} =
   result = SetLe(a, b) and not SetEq(a, b)
 
 proc SetMul(a, b: int): int {.compilerproc, asmNoStackFrame.} =
-  when defined(nimphp):
-    asm """
-      var $result = array();
-      foreach (`a` as $elem=>$_) {
-        if (isset(`b`[$elem])) { $result[$elem] = true; }
-      }
-      return $result;
-    """
-  else:
-    asm """
-      var result = {};
-      for (var elem in `a`) {
-        if (`b`[elem]) { result[elem] = true; }
-      }
-      return result;
-    """
+  asm """
+    var result = {};
+    for (var elem in `a`) {
+      if (`b`[elem]) { result[elem] = true; }
+    }
+    return result;
+  """
 
 proc SetPlus(a, b: int): int {.compilerproc, asmNoStackFrame.} =
-  when defined(nimphp):
-    asm """
-      var $result = array();
-      foreach (`a` as $elem=>$_) { $result[$elem] = true; }
-      foreach (`b` as $elem=>$_) { $result[$elem] = true; }
-      return $result;
-    """
-  else:
-    asm """
-      var result = {};
-      for (var elem in `a`) { result[elem] = true; }
-      for (var elem in `b`) { result[elem] = true; }
-      return result;
-    """
+  asm """
+    var result = {};
+    for (var elem in `a`) { result[elem] = true; }
+    for (var elem in `b`) { result[elem] = true; }
+    return result;
+  """
 
 proc SetMinus(a, b: int): int {.compilerproc, asmNoStackFrame.} =
-  when defined(nimphp):
-    asm """
-      $result = array();
-      foreach (`a` as $elem=>$_) {
-        if (!isset(`b`[$elem])) { $result[$elem] = true; }
-      }
-      return $result;
-    """
-  else:
-    asm """
-      var result = {};
-      for (var elem in `a`) {
-        if (!`b`[elem]) { result[elem] = true; }
-      }
-      return result;
-    """
+  asm """
+    var result = {};
+    for (var elem in `a`) {
+      if (!`b`[elem]) { result[elem] = true; }
+    }
+    return result;
+  """
 
 proc cmpStrings(a, b: string): int {.asmNoStackFrame, compilerProc.} =
   asm """
@@ -424,15 +332,8 @@ proc cmpStrings(a, b: string): int {.asmNoStackFrame, compilerProc.} =
     return `a`.length - `b`.length;
   """
 
-proc cmp(x, y: string): int =
-  when defined(nimphp):
-    asm """
-      if(`x` < `y`) `result` = -1;
-      elseif (`x` > `y`) `result` = 1;
-      else `result` = 0;
-    """
-  else:
-    return cmpStrings(x, y)
+proc cmp(x, y: string): int = 
+  return cmpStrings(x, y)
 
 proc eqStrings(a, b: string): bool {.asmNoStackFrame, compilerProc.} =
   asm """
@@ -467,7 +368,7 @@ elif not defined(nimOldEcho):
       console.log(buf);
     """
 
-elif not defined(nimphp):
+else:
   proc ewriteln(x: cstring) =
     var node : JSRef
     {.emit: "`node` = document.getElementsByTagName('body')[0];".}
@@ -493,127 +394,77 @@ elif not defined(nimphp):
 
 # Arithmetic:
 proc addInt(a, b: int): int {.asmNoStackFrame, compilerproc.} =
-  when defined(nimphp):
-    asm """
-      return `a` + `b`;
-    """
-  else:
-    asm """
-      var result = `a` + `b`;
-      if (result > 2147483647 || result < -2147483648) `raiseOverflow`();
-      return result;
-    """
+  asm """
+    var result = `a` + `b`;
+    if (result > 2147483647 || result < -2147483648) `raiseOverflow`();
+    return result;
+  """
 
 proc subInt(a, b: int): int {.asmNoStackFrame, compilerproc.} =
-  when defined(nimphp):
-    asm """
-      return `a` - `b`;
-    """
-  else:
-    asm """
-      var result = `a` - `b`;
-      if (result > 2147483647 || result < -2147483648) `raiseOverflow`();
-      return result;
-    """
+  asm """
+    var result = `a` - `b`;
+    if (result > 2147483647 || result < -2147483648) `raiseOverflow`();
+    return result;
+  """
 
 proc mulInt(a, b: int): int {.asmNoStackFrame, compilerproc.} =
-  when defined(nimphp):
-    asm """
-      return `a` * `b`;
-    """
-  else:
-    asm """
-      var result = `a` * `b`;
-      if (result > 2147483647 || result < -2147483648) `raiseOverflow`();
-      return result;
-    """
+  asm """
+    var result = `a` * `b`;
+    if (result > 2147483647 || result < -2147483648) `raiseOverflow`();
+    return result;
+  """
 
 proc divInt(a, b: int): int {.asmNoStackFrame, compilerproc.} =
-  when defined(nimphp):
-    asm """
-      return trunc(`a` / `b`);
-    """
-  else:
-    asm """
-      if (`b` == 0) `raiseDivByZero`();
-      if (`b` == -1 && `a` == 2147483647) `raiseOverflow`();
-      return Math.trunc(`a` / `b`);
-    """
+  asm """
+    if (`b` == 0) `raiseDivByZero`();
+    if (`b` == -1 && `a` == 2147483647) `raiseOverflow`();
+    return Math.trunc(`a` / `b`);
+  """
 
 proc modInt(a, b: int): int {.asmNoStackFrame, compilerproc.} =
-  when defined(nimphp):
-    asm """
-      return `a` % `b`;
-    """
-  else:
-    asm """
-      if (`b` == 0) `raiseDivByZero`();
-      if (`b` == -1 && `a` == 2147483647) `raiseOverflow`();
-      return Math.trunc(`a` % `b`);
-    """
+  asm """
+    if (`b` == 0) `raiseDivByZero`();
+    if (`b` == -1 && `a` == 2147483647) `raiseOverflow`();
+    return Math.trunc(`a` % `b`);
+  """
 
 proc addInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} =
-  when defined(nimphp):
-    asm """
-      return `a` + `b`;
-    """
-  else:
-    asm """
-      var result = `a` + `b`;
-      if (result > 9223372036854775807
-      || result < -9223372036854775808) `raiseOverflow`();
-      return result;
-    """
+  asm """
+    var result = `a` + `b`;
+    if (result > 9223372036854775807
+    || result < -9223372036854775808) `raiseOverflow`();
+    return result;
+  """
 
 proc subInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} =
-  when defined(nimphp):
-    asm """
-      return `a` - `b`;
-    """
-  else:
-    asm """
-      var result = `a` - `b`;
-      if (result > 9223372036854775807
-      || result < -9223372036854775808) `raiseOverflow`();
-      return result;
-    """
+  asm """
+    var result = `a` - `b`;
+    if (result > 9223372036854775807
+    || result < -9223372036854775808) `raiseOverflow`();
+    return result;
+  """
 
 proc mulInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} =
-  when defined(nimphp):
-    asm """
-      return `a` * `b`;
-    """
-  else:
-    asm """
-      var result = `a` * `b`;
-      if (result > 9223372036854775807
-      || result < -9223372036854775808) `raiseOverflow`();
-      return result;
-    """
+  asm """
+    var result = `a` * `b`;
+    if (result > 9223372036854775807
+    || result < -9223372036854775808) `raiseOverflow`();
+    return result;
+  """
 
 proc divInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} =
-  when defined(nimphp):
-    asm """
-      return trunc(`a` / `b`);
-    """
-  else:
-    asm """
-      if (`b` == 0) `raiseDivByZero`();
-      if (`b` == -1 && `a` == 9223372036854775807) `raiseOverflow`();
-      return Math.trunc(`a` / `b`);
-    """
+  asm """
+    if (`b` == 0) `raiseDivByZero`();
+    if (`b` == -1 && `a` == 9223372036854775807) `raiseOverflow`();
+    return Math.trunc(`a` / `b`);
+  """
 
 proc modInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} =
-  when defined(nimphp):
-    asm """
-      return `a` % `b`;
-    """
-  else:
-    asm """
-      if (`b` == 0) `raiseDivByZero`();
-      if (`b` == -1 && `a` == 9223372036854775807) `raiseOverflow`();
-      return Math.trunc(`a` % `b`);
-    """
+  asm """
+    if (`b` == 0) `raiseDivByZero`();
+    if (`b` == -1 && `a` == 9223372036854775807) `raiseOverflow`();
+    return Math.trunc(`a` % `b`);
+  """
 
 proc negInt(a: int): int {.compilerproc.} =
   result = a*(-1)
@@ -767,24 +618,14 @@ proc genericReset(x: JSRef, ti: PNimType): JSRef {.compilerproc.} =
   else:
     discard
 
-when defined(nimphp):
-  proc arrayConstr(len: int, value: string, typ: string): JSRef {.
-                  asmNoStackFrame, compilerproc.} =
-    # types are fake
-    asm """
-      $result = array();
-      for ($i = 0; $i < `len`; $i++) $result[] = `value`;
-      return $result;
-    """
-else:
-  proc arrayConstr(len: int, value: JSRef, typ: PNimType): JSRef {.
-                  asmNoStackFrame, compilerproc.} =
+proc arrayConstr(len: int, value: JSRef, typ: PNimType): JSRef {.
+                asmNoStackFrame, compilerproc.} =
   # types are fake
-    asm """
-      var result = new Array(`len`);
-      for (var i = 0; i < `len`; ++i) result[i] = nimCopy(null, `value`, `typ`);
-      return result;
-    """
+  asm """
+    var result = new Array(`len`);
+    for (var i = 0; i < `len`; ++i) result[i] = nimCopy(null, `value`, `typ`);
+    return result;
+  """
 
 proc chckIndx(i, a, b: int): int {.compilerproc.} =
   if i >= a and i <= b: return i
diff --git a/lib/system/osalloc.nim b/lib/system/osalloc.nim
index 1ad4cf695..9609b6d39 100644
--- a/lib/system/osalloc.nim
+++ b/lib/system/osalloc.nim
@@ -58,7 +58,7 @@ when defined(emscripten):
 
     # Convert pointer to PageSize correct one.
     var new_pos = cast[ByteAddress](pos) +% (PageSize - (pos %% PageSize))
-    if (new_pos-pos)< sizeof(EmscriptenMMapBlock):
+    if (new_pos-pos) < sizeof(EmscriptenMMapBlock):
       new_pos = new_pos +% PageSize
     result = cast[pointer](new_pos)
 
diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim
index ea0273340..c5c8a9cac 100644
--- a/lib/system/sysstr.nim
+++ b/lib/system/sysstr.nim
@@ -22,21 +22,34 @@ proc resize(old: int): int {.inline.} =
 
 proc cmpStrings(a, b: NimString): int {.inline, compilerProc.} =
   if a == b: return 0
-  if a == nil: return -1
-  if b == nil: return 1
-  let minlen = min(a.len, b.len)
+  when defined(nimNoNil):
+    let alen = if a == nil: 0 else: a.len
+    let blen = if b == nil: 0 else: b.len
+  else:
+    if a == nil: return -1
+    if b == nil: return 1
+    let alen = a.len
+    let blen = b.len
+  let minlen = min(alen, blen)
   if minlen > 0:
     result = c_memcmp(addr a.data, addr b.data, minlen.csize)
     if result == 0:
-      result = a.len - b.len
+      result = alen - blen
   else:
-    result = a.len - b.len
+    result = alen - blen
 
 proc eqStrings(a, b: NimString): bool {.inline, compilerProc.} =
   if a == b: return true
-  if a == nil or b == nil: return false
-  return a.len == b.len and
-    equalMem(addr(a.data), addr(b.data), a.len)
+  when defined(nimNoNil):
+    let alen = if a == nil: 0 else: a.len
+    let blen = if b == nil: 0 else: b.len
+  else:
+    if a == nil or b == nil: return false
+    let alen = a.len
+    let blen = b.len
+  if alen == blen:
+    if alen == 0: return true
+    return equalMem(addr(a.data), addr(b.data), alen)
 
 when declared(allocAtomic):
   template allocStr(size: untyped): untyped =
@@ -63,6 +76,7 @@ proc rawNewStringNoInit(space: int): NimString {.compilerProc.} =
   if s < 7: s = 7
   result = allocStrNoInit(sizeof(TGenericSeq) + s + 1)
   result.reserved = s
+  result.len = 0
   when defined(gogc):
     result.elemSize = 1
 
@@ -71,6 +85,7 @@ proc rawNewString(space: int): NimString {.compilerProc.} =
   if s < 7: s = 7
   result = allocStr(sizeof(TGenericSeq) + s + 1)
   result.reserved = s
+  result.len = 0
   when defined(gogc):
     result.elemSize = 1
 
@@ -101,9 +116,6 @@ proc cstrToNimstr(str: cstring): NimString {.compilerRtl.} =
   if str == nil: NimString(nil)
   else: toNimStr(str, str.len)
 
-template wasMoved(x: NimString): bool = false
-# (x.reserved and seqShallowFlag) != 0
-
 proc copyString(src: NimString): NimString {.compilerRtl.} =
   if src != nil:
     if (src.reserved and seqShallowFlag) != 0:
@@ -161,14 +173,16 @@ proc hashString(s: string): int {.compilerproc.} =
 
 proc addChar(s: NimString, c: char): NimString =
   # is compilerproc!
-  result = s
-  if result.len >= result.space:
-    let r = resize(result.space)
-    result = cast[NimString](growObj(result,
-      sizeof(TGenericSeq) + r + 1))
-    result.reserved = r
-  elif wasMoved(s):
-    result = newOwnedString(s, s.len)
+  if s == nil:
+    result = rawNewStringNoInit(1)
+    result.len = 0
+  else:
+    result = s
+    if result.len >= result.space:
+      let r = resize(result.space)
+      result = cast[NimString](growObj(result,
+        sizeof(TGenericSeq) + r + 1))
+      result.reserved = r
   result.data[result.len] = c
   result.data[result.len+1] = '\0'
   inc(result.len)
@@ -205,7 +219,9 @@ proc addChar(s: NimString, c: char): NimString =
 #   s = rawNewString(0);
 
 proc resizeString(dest: NimString, addlen: int): NimString {.compilerRtl.} =
-  if dest.len + addlen <= dest.space and not wasMoved(dest):
+  if dest == nil:
+    result = rawNewStringNoInit(addlen)
+  elif dest.len + addlen <= dest.space:
     result = dest
   else: # slow path:
     var sp = max(resize(dest.space), dest.len + addlen)
@@ -216,8 +232,9 @@ proc resizeString(dest: NimString, addlen: int): NimString {.compilerRtl.} =
     # DO NOT UPDATE LEN YET: dest.len = newLen
 
 proc appendString(dest, src: NimString) {.compilerproc, inline.} =
-  copyMem(addr(dest.data[dest.len]), addr(src.data), src.len + 1)
-  inc(dest.len, src.len)
+  if src != nil:
+    copyMem(addr(dest.data[dest.len]), addr(src.data), src.len + 1)
+    inc(dest.len, src.len)
 
 proc appendChar(dest: NimString, c: char) {.compilerproc, inline.} =
   dest.data[dest.len] = c
@@ -226,8 +243,8 @@ proc appendChar(dest: NimString, c: char) {.compilerproc, inline.} =
 
 proc setLengthStr(s: NimString, newLen: int): NimString {.compilerRtl.} =
   var n = max(newLen, 0)
-  if wasMoved(s):
-    result = newOwnedString(s, n)
+  if s == nil:
+    result = mnewString(newLen)
   elif n <= s.space:
     result = s
   else:
@@ -261,6 +278,18 @@ proc incrSeqV2(seq: PGenericSeq, elemSize: int): PGenericSeq {.compilerProc.} =
                                GenericSeqSize))
     result.reserved = r
 
+proc incrSeqV3(s: PGenericSeq, typ: PNimType): PGenericSeq {.compilerProc.} =
+  if s == nil:
+    result = cast[PGenericSeq](newSeq(typ, 1))
+    result.len = 0
+  else:
+    result = s
+    if result.len >= result.space:
+      let r = resize(result.space)
+      result = cast[PGenericSeq](growObj(result, typ.base.size * r +
+                                GenericSeqSize))
+      result.reserved = r
+
 proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {.
     compilerRtl, inl.} =
   result = seq
@@ -301,6 +330,13 @@ proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {.
            (newLen*%elemSize)), (result.len-%newLen) *% elemSize)
   result.len = newLen
 
+proc setLengthSeqV2(s: PGenericSeq, typ: PNimType, newLen: int): PGenericSeq {.
+    compilerRtl.} =
+  if s == nil:
+    result = cast[PGenericSeq](newSeq(typ, newLen))
+  else:
+    result = setLengthSeq(s, typ.base.size, newLen)
+
 # --------------- other string routines ----------------------------------
 proc add*(result: var string; x: int64) =
   let base = result.len
diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim
index f40344396..7e22f98c7 100644
--- a/lib/windows/winlean.nim
+++ b/lib/windows/winlean.nim
@@ -10,7 +10,7 @@
 ## This module implements a small wrapper for some needed Win API procedures,
 ## so that the Nim compiler does not depend on the huge Windows module.
 
-{.deadCodeElim:on.}
+{.deadCodeElim: on.}  # dce option deprecated
 
 import dynlib
 
@@ -1086,3 +1086,14 @@ proc ConvertThreadToFiberEx*(param: pointer, flags: int32): pointer {.stdcall, d
 proc DeleteFiber*(fiber: pointer): void {.stdcall, discardable, dynlib: "kernel32", importc.}
 proc SwitchToFiber*(fiber: pointer): void {.stdcall, discardable, dynlib: "kernel32", importc.}
 proc GetCurrentFiber*(): pointer {.stdcall, importc, header: "Windows.h".}
+
+proc toFILETIME*(t: int64): FILETIME =
+  ## Convert the Windows file time timestamp ``t`` to ``FILETIME``.
+  result = FILETIME(dwLowDateTime: cast[DWORD](t), dwHighDateTime: DWORD(t shr 32))
+
+type
+  LPFILETIME* = ptr FILETIME
+
+proc setFileTime*(hFile: HANDLE, lpCreationTime: LPFILETIME,
+                 lpLastAccessTime: LPFILETIME, lpLastWriteTime: LPFILETIME): WINBOOL
+     {.stdcall, dynlib: "kernel32", importc: "SetFileTime".}
diff --git a/lib/wrappers/iup.nim b/lib/wrappers/iup.nim
index d910173ca..dee2e14c7 100644
--- a/lib/wrappers/iup.nim
+++ b/lib/wrappers/iup.nim
@@ -34,7 +34,7 @@
 # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 # ****************************************************************************
 
-{.deadCodeElim: on.}
+{.deadCodeElim: on.}  # dce option deprecated
 
 when defined(windows):
   const dllname = "iup(|30|27|26|25|24).dll"
diff --git a/lib/wrappers/mysql.nim b/lib/wrappers/mysql.nim
index 4464eae50..06c663822 100644
--- a/lib/wrappers/mysql.nim
+++ b/lib/wrappers/mysql.nim
@@ -7,7 +7,7 @@
 #    distribution, for details about the copyright.
 #
 
-{.deadCodeElim: on.}
+{.deadCodeElim: on.}  # dce option deprecated
 {.push, callconv: cdecl.}
 
 when defined(Unix):
diff --git a/lib/wrappers/odbcsql.nim b/lib/wrappers/odbcsql.nim
index 1b2544ec0..43d1c504d 100644
--- a/lib/wrappers/odbcsql.nim
+++ b/lib/wrappers/odbcsql.nim
@@ -7,7 +7,7 @@
 #    distribution, for details about the copyright.
 #
 
-{.deadCodeElim: on.}
+{.deadCodeElim: on.}  # dce option deprecated
 
 when not defined(ODBCVER):
   const
diff --git a/lib/wrappers/openssl.nim b/lib/wrappers/openssl.nim
index 357343bff..6609dc245 100644
--- a/lib/wrappers/openssl.nim
+++ b/lib/wrappers/openssl.nim
@@ -22,7 +22,7 @@
 ##   ./bin/nim c -d:ssl -p:. -r tests/untestable/tssl.nim
 ##   ./bin/nim c -d:ssl -p:. --dynlibOverride:ssl --passL:-lcrypto --passL:-lssl -r tests/untestable/tssl.nim
 
-{.deadCodeElim: on.}
+{.deadCodeElim: on.}  # dce option deprecated
 
 const useWinVersion = defined(Windows) or defined(nimdoc)
 
@@ -38,7 +38,7 @@ when useWinVersion:
 
   from winlean import SocketHandle
 else:
-  const versions = "(.1.1|.38|.39|.41|.43|.44|.10|.1.0.2|.1.0.1|.1.0.0|.0.9.9|.0.9.8|)"
+  const versions = "(.1.1|.38|.39|.41|.43|.44|.45|.10|.1.0.2|.1.0.1|.1.0.0|.0.9.9|.0.9.8|)"
 
   when defined(macosx):
     const
diff --git a/lib/wrappers/pcre.nim b/lib/wrappers/pcre.nim
index 6c7088bbf..e9e11960c 100644
--- a/lib/wrappers/pcre.nim
+++ b/lib/wrappers/pcre.nim
@@ -7,7 +7,7 @@
 #    distribution, for details about the copyright.
 #
 
-{.deadCodeElim: on.}
+{.deadCodeElim: on.}  # dce option deprecated
 
 # The current PCRE version information.
 
diff --git a/lib/wrappers/postgres.nim b/lib/wrappers/postgres.nim
index f9a10dccb..7cb816f68 100644
--- a/lib/wrappers/postgres.nim
+++ b/lib/wrappers/postgres.nim
@@ -15,7 +15,7 @@
 # connection-protocol.
 #
 
-{.deadCodeElim: on.}
+{.deadCodeElim: on.}  # dce option deprecated
 
 when defined(windows):
   const
diff --git a/lib/wrappers/sqlite3.nim b/lib/wrappers/sqlite3.nim
index a12945832..0276a0a65 100644
--- a/lib/wrappers/sqlite3.nim
+++ b/lib/wrappers/sqlite3.nim
@@ -7,7 +7,7 @@
 #    distribution, for details about the copyright.
 #
 
-{.deadCodeElim: on.}
+{.deadCodeElim: on.}  # dce option deprecated
 when defined(windows):
   when defined(nimOldDlls):
     const Lib = "sqlite3.dll"
diff --git a/nimsuggest/nimsuggest.nim b/nimsuggest/nimsuggest.nim
index 49e61b9a9..5c505ddf7 100644
--- a/nimsuggest/nimsuggest.nim
+++ b/nimsuggest/nimsuggest.nim
@@ -158,9 +158,10 @@ proc symFromInfo(graph: ModuleGraph; gTrackPos: TLineInfo): PSym =
 
 proc execute(cmd: IdeCmd, file, dirtyfile: string, line, col: int;
              graph: ModuleGraph; cache: IdentCache) =
+  let conf = graph.config
   myLog("cmd: " & $cmd & ", file: " & file & ", dirtyFile: " & dirtyfile &
         "[" & $line & ":" & $col & "]")
-  gIdeCmd = cmd
+  conf.ideCmd = cmd
   if cmd == ideChk:
     msgs.structuredErrorHook = errorHook
     msgs.writelnHook = myLog
@@ -170,33 +171,33 @@ proc execute(cmd: IdeCmd, file, dirtyfile: string, line, col: int;
   if cmd == ideUse and suggestVersion != 0:
     graph.resetAllModules()
   var isKnownFile = true
-  let dirtyIdx = file.fileInfoIdx(isKnownFile)
+  let dirtyIdx = fileInfoIdx(conf, file, isKnownFile)
 
   if dirtyfile.len != 0: msgs.setDirtyFile(dirtyIdx, dirtyfile)
   else: msgs.setDirtyFile(dirtyIdx, nil)
 
   gTrackPos = newLineInfo(dirtyIdx, line, col)
   gTrackPosAttached = false
-  gErrorCounter = 0
+  conf.errorCounter = 0
   if suggestVersion == 1:
     graph.usageSym = nil
   if not isKnownFile:
     graph.compileProject(cache)
-  if suggestVersion == 0 and gIdeCmd in {ideUse, ideDus} and
+  if suggestVersion == 0 and conf.ideCmd in {ideUse, ideDus} and
       dirtyfile.len == 0:
     discard "no need to recompile anything"
   else:
     let modIdx = graph.parentModule(dirtyIdx)
     graph.markDirty dirtyIdx
     graph.markClientsDirty dirtyIdx
-    if gIdeCmd != ideMod:
+    if conf.ideCmd != ideMod:
       graph.compileProject(cache, modIdx)
-  if gIdeCmd in {ideUse, ideDus}:
+  if conf.ideCmd in {ideUse, ideDus}:
     let u = if suggestVersion != 1: graph.symFromInfo(gTrackPos) else: graph.usageSym
     if u != nil:
-      listUsages(u)
+      listUsages(conf, u)
     else:
-      localError(gTrackPos, "found no symbol at this position " & $gTrackPos)
+      localError(conf, gTrackPos, "found no symbol at this position " & $gTrackPos)
 
 proc executeEpc(cmd: IdeCmd, args: SexpNode;
                 graph: ModuleGraph; cache: IdentCache) =
@@ -257,7 +258,7 @@ proc toEpc(client: Socket; uid: BiggestInt) {.gcsafe.} =
 
 template setVerbosity(level: typed) =
   gVerbosity = level
-  gNotes = NotesVerbosity[gVerbosity]
+  conf.notes = NotesVerbosity[gVerbosity]
 
 proc connectToNextFreePort(server: Socket, host: string): Port =
   server.bindaddr(Port(0), host)
@@ -349,16 +350,18 @@ proc replEpc(x: ThreadParams) {.thread.} =
     of "call":
       let
         uid = message[1].getNum
+        cmd = message[2].getSymbol
         args = message[3]
 
-      gIdeCmd = parseIdeCmd(message[2].getSymbol)
-      case gIdeCmd
-      of ideSug, ideCon, ideDef, ideUse, ideDus, ideOutline, ideHighlight:
-        setVerbosity(0)
-      else: discard
-      let cmd = $gIdeCmd & " " & args.argsToStr
-      myLog "MSG CMD: " & cmd
-      requests.send(cmd)
+      when false:
+        x.ideCmd[] = parseIdeCmd(message[2].getSymbol)
+        case x.ideCmd[]
+        of ideSug, ideCon, ideDef, ideUse, ideDus, ideOutline, ideHighlight:
+          setVerbosity(0)
+        else: discard
+      let fullCmd = cmd & " " & args.argsToStr
+      myLog "MSG CMD: " & fullCmd
+      requests.send(fullCmd)
       toEpc(client, uid)
     of "methods":
       returnEpc(client, message[1].getNum, listEpc())
@@ -375,15 +378,17 @@ proc replEpc(x: ThreadParams) {.thread.} =
       quit errMessage
 
 proc execCmd(cmd: string; graph: ModuleGraph; cache: IdentCache; cachedMsgs: CachedMsgs) =
+  let conf = graph.config
+
   template sentinel() =
     # send sentinel for the input reading thread:
     results.send(Suggest(section: ideNone))
 
   template toggle(sw) =
-    if sw in gGlobalOptions:
-      excl(gGlobalOptions, sw)
+    if sw in conf.globalOptions:
+      excl(conf.globalOptions, sw)
     else:
-      incl(gGlobalOptions, sw)
+      incl(conf.globalOptions, sw)
     sentinel()
     return
 
@@ -395,21 +400,21 @@ proc execCmd(cmd: string; graph: ModuleGraph; cache: IdentCache; cachedMsgs: Cac
   var opc = ""
   var i = parseIdent(cmd, opc, 0)
   case opc.normalize
-  of "sug": gIdeCmd = ideSug
-  of "con": gIdeCmd = ideCon
-  of "def": gIdeCmd = ideDef
-  of "use": gIdeCmd = ideUse
-  of "dus": gIdeCmd = ideDus
-  of "mod": gIdeCmd = ideMod
-  of "chk": gIdeCmd = ideChk
-  of "highlight": gIdeCmd = ideHighlight
-  of "outline": gIdeCmd = ideOutline
+  of "sug": conf.ideCmd = ideSug
+  of "con": conf.ideCmd = ideCon
+  of "def": conf.ideCmd = ideDef
+  of "use": conf.ideCmd = ideUse
+  of "dus": conf.ideCmd = ideDus
+  of "mod": conf.ideCmd = ideMod
+  of "chk": conf.ideCmd = ideChk
+  of "highlight": conf.ideCmd = ideHighlight
+  of "outline": conf.ideCmd = ideOutline
   of "quit":
     sentinel()
     quit()
   of "debug": toggle optIdeDebug
   of "terse": toggle optIdeTerse
-  of "known": gIdeCmd = ideKnown
+  of "known": conf.ideCmd = ideKnown
   else: err()
   var dirtyfile = ""
   var orig = ""
@@ -423,17 +428,17 @@ proc execCmd(cmd: string; graph: ModuleGraph; cache: IdentCache; cachedMsgs: Cac
   i += skipWhile(cmd, seps, i)
   i += parseInt(cmd, col, i)
 
-  if gIdeCmd == ideKnown:
-    results.send(Suggest(section: ideKnown, quality: ord(fileInfoKnown(orig))))
+  if conf.ideCmd == ideKnown:
+    results.send(Suggest(section: ideKnown, quality: ord(fileInfoKnown(conf, orig))))
   else:
-    if gIdeCmd == ideChk:
+    if conf.ideCmd == ideChk:
       for cm in cachedMsgs: errorHook(cm.info, cm.msg, cm.sev)
-    execute(gIdeCmd, orig, dirtyfile, line, col, graph, cache)
+    execute(conf.ideCmd, orig, dirtyfile, line, col, graph, cache)
   sentinel()
 
 proc recompileFullProject(graph: ModuleGraph; cache: IdentCache) =
   #echo "recompiling full project"
-  resetSystemArtifacts()
+  resetSystemArtifacts(graph)
   vm.globalCtx = nil
   graph.resetAllModules()
   GC_fullcollect()
@@ -441,8 +446,9 @@ proc recompileFullProject(graph: ModuleGraph; cache: IdentCache) =
   #echo GC_getStatistics()
 
 proc mainThread(graph: ModuleGraph; cache: IdentCache) =
+  let conf = graph.config
   if gLogging:
-    for it in searchPaths:
+    for it in conf.searchPaths:
       log(it)
 
   proc wrHook(line: string) {.closure.} =
@@ -468,7 +474,7 @@ proc mainThread(graph: ModuleGraph; cache: IdentCache) =
       idle += 1
     if idle == 20 and gRefresh:
       # we use some nimsuggest activity to enable a lazy recompile:
-      gIdeCmd = ideChk
+      conf.ideCmd = ideChk
       msgs.writelnHook = proc (s: string) = discard
       cachedMsgs.setLen 0
       msgs.structuredErrorHook = proc (info: TLineInfo; msg: string; sev: Severity) =
@@ -480,20 +486,21 @@ var
   inputThread: Thread[ThreadParams]
 
 proc mainCommand(graph: ModuleGraph; cache: IdentCache) =
+  let conf = graph.config
   clearPasses()
   registerPass verbosePass
   registerPass semPass
-  gCmd = cmdIdeTools
-  incl gGlobalOptions, optCaasEnabled
-  wantMainModule()
+  conf.cmd = cmdIdeTools
+  incl conf.globalOptions, optCaasEnabled
+  wantMainModule(conf)
 
-  if not fileExists(gProjectFull):
-    quit "cannot find file: " & gProjectFull
+  if not fileExists(conf.projectFull):
+    quit "cannot find file: " & conf.projectFull
 
-  add(searchPaths, options.libpath)
+  add(conf.searchPaths, conf.libpath)
 
   # do not stop after the first error:
-  msgs.gErrorMax = high(int)
+  conf.errorMax = high(int)
   # do not print errors, but log them
   msgs.writelnHook = proc (s: string) = log(s)
   msgs.structuredErrorHook = nil
@@ -510,15 +517,15 @@ proc mainCommand(graph: ModuleGraph; cache: IdentCache) =
   of mtcp: createThread(inputThread, replTcp, (gPort, gAddress))
   of mepc: createThread(inputThread, replEpc, (gPort, gAddress))
   of mcmdsug: createThread(inputThread, replCmdline,
-                            (gPort, "sug \"" & options.gProjectFull & "\":" & gAddress))
+                            (gPort, "sug \"" & conf.projectFull & "\":" & gAddress))
   of mcmdcon: createThread(inputThread, replCmdline,
-                            (gPort, "con \"" & options.gProjectFull & "\":" & gAddress))
+                            (gPort, "con \"" & conf.projectFull & "\":" & gAddress))
   mainThread(graph, cache)
   joinThread(inputThread)
   close(requests)
   close(results)
 
-proc processCmdLine*(pass: TCmdLinePass, cmd: string) =
+proc processCmdLine*(pass: TCmdLinePass, cmd: string; conf: ConfigRef) =
   var p = parseopt.initOptParser(cmd)
   while true:
     parseopt.next(p)
@@ -539,15 +546,15 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string) =
       of "cmdsug":
         gMode = mcmdsug
         gAddress = p.val
-        incl(gGlobalOptions, optIdeDebug)
+        incl(conf.globalOptions, optIdeDebug)
       of "cmdcon":
         gMode = mcmdcon
         gAddress = p.val
-        incl(gGlobalOptions, optIdeDebug)
+        incl(conf.globalOptions, optIdeDebug)
       of "epc":
         gMode = mepc
-        gVerbosity = 0          # Port number gotta be first.
-      of "debug": incl(gGlobalOptions, optIdeDebug)
+        conf.verbosity = 0          # Port number gotta be first.
+      of "debug": incl(conf.globalOptions, optIdeDebug)
       of "v2": suggestVersion = 0
       of "v1": suggestVersion = 1
       of "tester":
@@ -562,66 +569,67 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string) =
           gRefresh = true
       of "maxresults":
         suggestMaxResults = parseInt(p.val)
-      else: processSwitch(pass, p)
+      else: processSwitch(pass, p, conf)
     of cmdArgument:
       let a = unixToNativePath(p.key)
       if dirExists(a) and not fileExists(a.addFileExt("nim")):
-        options.gProjectName = findProjectNimFile(a)
+        conf.projectName = findProjectNimFile(conf, a)
         # don't make it worse, report the error the old way:
-        if options.gProjectName.len == 0: options.gProjectName = a
+        if conf.projectName.len == 0: conf.projectName = a
       else:
-        options.gProjectName = a
+        conf.projectName = a
       # if processArgument(pass, p, argsCount): break
 
-proc handleCmdLine(cache: IdentCache; config: ConfigRef) =
+proc handleCmdLine(cache: IdentCache; conf: ConfigRef) =
+  condsyms.initDefines(conf.symbols)
+  defineSymbol conf.symbols, "nimsuggest"
+
   if paramCount() == 0:
     stdout.writeline(Usage)
   else:
-    processCmdLine(passCmd1, "")
+    processCmdLine(passCmd1, "", conf)
     if gMode != mstdin:
       msgs.writelnHook = proc (msg: string) = discard
-    if gProjectName != "":
+    if conf.projectName != "":
       try:
-        gProjectFull = canonicalizePath(gProjectName)
+        conf.projectFull = canonicalizePath(conf, conf.projectName)
       except OSError:
-        gProjectFull = gProjectName
-      var p = splitFile(gProjectFull)
-      gProjectPath = canonicalizePath p.dir
-      gProjectName = p.name
+        conf.projectFull = conf.projectName
+      var p = splitFile(conf.projectFull)
+      conf.projectPath = canonicalizePath(conf, p.dir)
+      conf.projectName = p.name
     else:
-      gProjectPath = canonicalizePath getCurrentDir()
+      conf.projectPath = canonicalizePath(conf, getCurrentDir())
 
     # Find Nim's prefix dir.
     let binaryPath = findExe("nim")
     if binaryPath == "":
       raise newException(IOError,
           "Cannot find Nim standard library: Nim compiler not in PATH")
-    gPrefixDir = binaryPath.splitPath().head.parentDir()
-    if not dirExists(gPrefixDir / "lib"): gPrefixDir = ""
+    conf.prefixDir = binaryPath.splitPath().head.parentDir()
+    if not dirExists(conf.prefixDir / "lib"): conf.prefixDir = ""
 
     #msgs.writelnHook = proc (line: string) = log(line)
-    myLog("START " & gProjectFull)
+    myLog("START " & conf.projectFull)
 
-    loadConfigs(DefaultConfig, cache, config) # load all config files
+    loadConfigs(DefaultConfig, cache, conf) # load all config files
     # now process command line arguments again, because some options in the
     # command line can overwite the config file's settings
-    options.command = "nimsuggest"
-    let scriptFile = gProjectFull.changeFileExt("nims")
+    conf.command = "nimsuggest"
+    let scriptFile = conf.projectFull.changeFileExt("nims")
     if fileExists(scriptFile):
       # 'nimsuggest foo.nims' means to just auto-complete the NimScript file:
-      if scriptFile != gProjectFull:
-        runNimScript(cache, scriptFile, freshDefines=false, config)
-    elif fileExists(gProjectPath / "config.nims"):
+      if scriptFile != conf.projectFull:
+        runNimScript(cache, scriptFile, freshDefines=false, conf)
+    elif fileExists(conf.projectPath / "config.nims"):
       # directory wide NimScript file
-      runNimScript(cache, gProjectPath / "config.nims", freshDefines=false, config)
+      runNimScript(cache, conf.projectPath / "config.nims", freshDefines=false, conf)
 
-    extccomp.initVars()
-    processCmdLine(passCmd2, "")
+    extccomp.initVars(conf)
+    processCmdLine(passCmd2, "", conf)
 
-    let graph = newModuleGraph(config)
+    let graph = newModuleGraph(conf)
     graph.suggestMode = true
     mainCommand(graph, cache)
 
-condsyms.initDefines()
-defineSymbol "nimsuggest"
 handleCmdline(newIdentCache(), newConfigRef())
diff --git a/tests/array/tarraycons_ptr_generic.nim b/tests/array/tarraycons_ptr_generic.nim
new file mode 100644
index 000000000..eb89a196f
--- /dev/null
+++ b/tests/array/tarraycons_ptr_generic.nim
@@ -0,0 +1,51 @@
+discard """
+  output: '''apple
+banana
+Fruit
+2
+4
+3
+none
+skin
+paper
+'''
+"""
+type
+  Fruit = object of RootObj
+    name: string
+  Apple = object of Fruit
+  Banana = object of Fruit
+
+var
+  ir = Fruit(name: "Fruit")
+  ia = Apple(name: "apple")
+  ib = Banana(name: "banana")
+
+let x = [ia.addr, ib.addr, ir.addr]
+for c in x: echo c.name
+
+type
+  Vehicle[T] = object of RootObj
+    tire: T
+  Car[T] = object of Vehicle[T]
+  Bike[T] = object of Vehicle[T]
+
+var v = Vehicle[int](tire: 3)
+var c = Car[int](tire: 4)
+var b = Bike[int](tire: 2)
+
+let y = [b.addr, c.addr, v.addr]
+for c in y: echo c.tire
+
+type
+  Book[T] = ref object of RootObj
+    cover: T
+  Hard[T] = ref object of Book[T]
+  Soft[T] = ref object of Book[T]
+
+var bn = Book[string](cover: "none")
+var hs = Hard[string](cover: "skin")
+var bp = Soft[string](cover: "paper")
+
+let z = [bn, hs, bp]
+for c in z: echo c.cover
diff --git a/tests/array/tarraycons_ptr_generic2.nim b/tests/array/tarraycons_ptr_generic2.nim
new file mode 100644
index 000000000..fce7af669
--- /dev/null
+++ b/tests/array/tarraycons_ptr_generic2.nim
@@ -0,0 +1,17 @@
+discard """
+  file: "tarraycons_ptr_generic2.nim"
+  line: 17
+  errormsg: "type mismatch: got <ptr Hard[system.string]> but expected 'Book[system.string]'"
+"""
+
+type
+  Book[T] = ref object of RootObj
+    cover: T
+  Hard[T] = ref object of Book[T]
+  Soft[T] = ref object of Book[T]
+
+var bn = Book[string](cover: "none")
+var hs = Hard[string](cover: "skin")
+var bp = Soft[string](cover: "paper")
+
+let z = [bn, hs.addr, bp]
diff --git a/tests/assign/toverload_asgn1.nim b/tests/assign/toverload_asgn1.nim
index dbc3a71c4..01e7e7aa7 100644
--- a/tests/assign/toverload_asgn1.nim
+++ b/tests/assign/toverload_asgn1.nim
@@ -14,6 +14,7 @@ GenericT[T] '=' bool
 GenericT[T] '=' bool
 GenericT[T] '=' bool
 GenericT[T] '=' bool'''
+  disabled: "true"
 """
 
 import typetraits
diff --git a/tests/assign/toverload_asgn2.nim b/tests/assign/toverload_asgn2.nim
index 243c90494..1104be92b 100644
--- a/tests/assign/toverload_asgn2.nim
+++ b/tests/assign/toverload_asgn2.nim
@@ -1,6 +1,7 @@
 discard """
   output: '''i value 88
 2aa'''
+  disabled: "true"
 """
 
 import moverload_asgn2
diff --git a/tests/async/tasyncawait.nim b/tests/async/tasyncawait.nim
index 9fe9507ad..74933f063 100644
--- a/tests/async/tasyncawait.nim
+++ b/tests/async/tasyncawait.nim
@@ -12,11 +12,11 @@ const
 
 var clientCount = 0
 
-proc sendMessages(client: TAsyncFD) {.async.} =
+proc sendMessages(client: AsyncFD) {.async.} =
   for i in 0 .. <messagesToSend:
     await send(client, "Message " & $i & "\c\L")
 
-proc launchSwarm(port: TPort) {.async.} =
+proc launchSwarm(port: Port) {.async.} =
   for i in 0 .. <swarmSize:
     var sock = newAsyncNativeSocket()
 
@@ -24,7 +24,7 @@ proc launchSwarm(port: TPort) {.async.} =
     await sendMessages(sock)
     closeSocket(sock)
 
-proc readMessages(client: TAsyncFD) {.async.} =
+proc readMessages(client: AsyncFD) {.async.} =
   while true:
     var line = await recvLine(client)
     if line == "":
@@ -37,7 +37,7 @@ proc readMessages(client: TAsyncFD) {.async.} =
       else:
         doAssert false
 
-proc createServer(port: TPort) {.async.} =
+proc createServer(port: Port) {.async.} =
   var server = newAsyncNativeSocket()
   block:
     var name: Sockaddr_in
@@ -55,8 +55,8 @@ proc createServer(port: TPort) {.async.} =
   while true:
     asyncCheck readMessages(await accept(server))
 
-asyncCheck createServer(TPort(10335))
-asyncCheck launchSwarm(TPort(10335))
+asyncCheck createServer(Port(10335))
+asyncCheck launchSwarm(Port(10335))
 while true:
   poll()
   if clientCount == swarmSize: break
diff --git a/tests/async/tlambda.nim b/tests/async/tlambda.nim
index d187c0d50..8f570689b 100644
--- a/tests/async/tlambda.nim
+++ b/tests/async/tlambda.nim
@@ -1,7 +1,7 @@
 
 # bug 2007
 
-import asyncdispatch, asyncnet, logging, json, uri, strutils, future
+import asyncdispatch, asyncnet, logging, json, uri, strutils, sugar
 
 type
   Builder = ref object
@@ -27,7 +27,7 @@ proc newBuild*(onProgress: ProgressCB): Build =
   result.onProgress = onProgress
 
 proc start(build: Build, repo, hash: string) {.async.} =
-  let path = repo.parseUri().path.toLower()
+  let path = repo.parseUri().path.toLowerAscii()
 
 proc onProgress(builder: Builder, message: string) {.async.} =
   debug($message)
diff --git a/tests/collections/tcollections_to_string.nim b/tests/collections/tcollections_to_string.nim
index 6cc8a84ff..48b06a6aa 100644
--- a/tests/collections/tcollections_to_string.nim
+++ b/tests/collections/tcollections_to_string.nim
@@ -85,14 +85,20 @@ block:
   s.addQuoted('\0')
   s.addQuoted('\31')
   s.addQuoted('\127')
-  s.addQuoted('\255')
-  doAssert s == "'\\x00''\\x1F''\\x7F''\\xFF'"
+  doAssert s == "'\\x00''\\x1F''\\x7F'"
 block:
   var s = ""
   s.addQuoted('\\')
   s.addQuoted('\'')
   s.addQuoted('\"')
   doAssert s == """'\\''\'''\"'"""
+block:
+  var s = ""
+  s.addQuoted("å")
+  s.addQuoted("ä")
+  s.addQuoted("ö")
+  s.addEscapedChar('\xFF')
+  doAssert s == """"å""ä""ö"\xFF"""
 
 # Test customized element representation
 type CustomString = object
diff --git a/tests/collections/thashes.nim b/tests/collections/thashes.nim
index 76b99313c..5cc3cc8bb 100644
--- a/tests/collections/thashes.nim
+++ b/tests/collections/thashes.nim
@@ -3,7 +3,7 @@ discard """
 """
 
 import tables
-from hashes import THash
+from hashes import Hash
 
 # Test with int
 block:
@@ -66,7 +66,7 @@ block:
 # The same test with a custom hash(s: string) does
 # work though.
 block:
-  proc hash(x: int): THash {.inline.} =
+  proc hash(x: int): Hash {.inline.} =
     echo "overloaded hash"
     result = x
   var t = initTable[int, int]()
diff --git a/tests/collections/tsets.nim b/tests/collections/tsets.nim
index 6139560bd..61e14260a 100644
--- a/tests/collections/tsets.nim
+++ b/tests/collections/tsets.nim
@@ -1,4 +1,6 @@
 import sets
+import hashes
+import algorithm
 
 block setEquality:
   var
@@ -35,7 +37,7 @@ block setWithSequences:
   doAssert( not s.contains(@[4, 5, 6]) )
 
 block setClearWorked:
-  var s = initSet[char]() 
+  var s = initSet[char]()
 
   for c in "this is a test":
     s.incl(c)
@@ -68,12 +70,54 @@ block orderedSetClearWorked:
   for c in "eat at joes":
     s.incl(c)
 
-  r = "" 
+  r = ""
   for c in items(s):
     add(r, c)
 
   doAssert r == "zeat jos"
 
+block hashForHashedSet:
+  let
+    seq1 = "This is the test."
+    seq2 = "the test is This."
+    s1 = seq1.toSet()
+    s2 = seq2.toSet()
+  var hashSeq: seq[Hash] = @[]
+  doAssert s1 == s2
+  doAssert hash(s1) == hash(s2)
+
+block hashForOrderdSet:
+  let
+    str = "This is the test."
+    rstr = str.reversed
 
-
-
+  var
+    s1 = initOrderedSet[char]()
+    s2 = initOrderedSet[char]()
+    r = initOrderedSet[char]()
+    expected: Hash
+    added: seq[char] = @[]
+    reversed: Hash
+    radded: seq[char] = @[]
+
+  expected = 0
+  for c in str:
+    if (not (c in added)):
+      expected = expected !& hash(c)
+      added.add(c)
+    s1.incl(c)
+    s2.incl(c)
+  expected = !$expected
+  doAssert hash(s1) == expected
+  doAssert hash(s1) == hash(s2)
+  doAssert hash(s1) != hash(r)
+
+  reversed = 0
+  for c in rstr:
+    if (not (c in radded)):
+      reversed = reversed !& hash(c)
+      radded.add(c)
+    r.incl(c)
+  reversed = !$reversed
+  doAssert hash(r) == reversed
+  doAssert hash(s1) != reversed
diff --git a/tests/compiles/trecursive_generic_in_compiles.nim b/tests/compiles/trecursive_generic_in_compiles.nim
index 77bf0bb02..9c7fd10b3 100644
--- a/tests/compiles/trecursive_generic_in_compiles.nim
+++ b/tests/compiles/trecursive_generic_in_compiles.nim
@@ -1,6 +1,6 @@
 # bug #3313
-import unittest, future
-
+import unittest, sugar
+{.experimental: "notnil".}
 type
   ListNodeKind = enum
     lnkNil, lnkCons
diff --git a/tests/concepts/tstackconcept.nim b/tests/concepts/tstackconcept.nim
index 2238dacb6..cb8db566d 100644
--- a/tests/concepts/tstackconcept.nim
+++ b/tests/concepts/tstackconcept.nim
@@ -31,7 +31,7 @@ type
     s.pop() is T
 
     type ValueType = T
-    const ValueTypeName = T.name.toUpper
+    const ValueTypeName = T.name.toUpperAscii
 
 proc genericAlgorithm[T](s: var Stack[T], y: T) =
   static:
diff --git a/tests/constructors/tinvalid_construction.nim b/tests/constructors/tinvalid_construction.nim
index bb3b1bebb..b3e56eec6 100644
--- a/tests/constructors/tinvalid_construction.nim
+++ b/tests/constructors/tinvalid_construction.nim
@@ -3,12 +3,12 @@ template accept(x) =
 
 template reject(x) =
   static: assert(not compiles(x))
-
+{.experimental: "notnil".}
 type
   TRefObj = ref object
     x: int
 
-  THasNotNils = object of TObject
+  THasNotNils = object of RootObj
     a: TRefObj not nil
     b: TRefObj not nil
     c: TRefObj
diff --git a/tests/controlflow/tstatret.nim b/tests/controlflow/tstatret.nim
index d655f5595..04cac9966 100644
--- a/tests/controlflow/tstatret.nim
+++ b/tests/controlflow/tstatret.nim
@@ -1,7 +1,7 @@
 discard """
   file: "tstatret.nim"
   line: 9
-  errormsg: "statement not allowed after"
+  errormsg: "unreachable statement after 'return'"
 """
 # no statement after return
 proc main() =
diff --git a/tests/converter/tconvert.nim b/tests/converter/tconvert.nim
index a37140234..48367a85b 100644
--- a/tests/converter/tconvert.nim
+++ b/tests/converter/tconvert.nim
@@ -15,6 +15,6 @@ type TFoo = object
 converter toPtr*(some: var TFoo): ptr TFoo = (addr some)
 
 
-proc zoot(x: ptr TFoo) = nil
+proc zoot(x: ptr TFoo) = discard
 var x: Tfoo
 zoot(x)
diff --git a/tests/cpp/tcovariancerules.nim b/tests/cpp/tcovariancerules.nim
index f81d67a50..acde1b288 100644
--- a/tests/cpp/tcovariancerules.nim
+++ b/tests/cpp/tcovariancerules.nim
@@ -13,14 +13,8 @@ cat
 cat
 dog
 dog
-dog value
-cat value
-dog value
-cat value
 dog
 dog
-dog value
-cat value
 dog 1
 dog 2
 '''
@@ -243,11 +237,12 @@ reject modifiesCovariantArray(dogRefsArray.addr)
 
 var dogValues = @[vdog, vdog]
 var dogValuesArray = [vdog, vdog]
-var animalValues = @[Animal(vdog), Animal(vcat)]
-var animalValuesArray = [Animal(vdog), Animal(vcat)]
+when false:
+  var animalValues = @[Animal(vdog), Animal(vcat)]
+  var animalValuesArray = [Animal(vdog), Animal(vcat)]
 
-wantsNonCovariantSeq animalValues
-wantsNonCovariantArray animalValuesArray
+  wantsNonCovariantSeq animalValues
+  wantsNonCovariantArray animalValuesArray
 
 reject wantsNonCovariantSeq(dogRefs)
 reject modifiesCovariantOperArray(dogRefs)
@@ -260,7 +255,6 @@ modifiesDerivedOperArray dogRefs
 reject modifiesDerivedOperArray(dogValues)
 reject modifiesDerivedOperArray(animalRefs)
 
-wantsNonCovariantOperArray animalValues
 reject wantsNonCovariantOperArray(animalRefs)
 reject wantsNonCovariantOperArray(dogRefs)
 reject wantsNonCovariantOperArray(dogValues)
diff --git a/tests/distinct/tnil.nim b/tests/distinct/tnil.nim
index e60437a1f..759a14657 100644
--- a/tests/distinct/tnil.nim
+++ b/tests/distinct/tnil.nim
@@ -1,15 +1,11 @@
 discard """
   file: "tnil.nim"
-  output: '''0x1
-
-nil
-
-nil
-
+  output: '''1
+0
+0
 '''
-  disabled: "windows"
 """
-
+{.experimental: "notnil".}
 type
   MyPointer = distinct pointer
   MyString = distinct string
@@ -17,7 +13,8 @@ type
   MyInt = distinct int
 
 proc foo(a: MyPointer) =
-  echo a.repr
+  # workaround a Windows 'repr' difference:
+  echo cast[int](a)
 
 foo(cast[MyPointer](1))
 foo(cast[MyPointer](nil))
diff --git a/tests/effects/teffects4.nim b/tests/effects/teffects4.nim
index fd5dd49e2..d0960126f 100644
--- a/tests/effects/teffects4.nim
+++ b/tests/effects/teffects4.nim
@@ -12,7 +12,7 @@ type
   EIO2 = ref object of EIO
 
 proc q() {.tags: [FIO].} =
-  nil
+  discard
 
 proc raiser(): int =
   writeLine stdout, "arg"
diff --git a/tests/enum/toptions.nim b/tests/enum/toptions.nim
index e53acb2b3..da66f0067 100644
--- a/tests/enum/toptions.nim
+++ b/tests/enum/toptions.nim
@@ -4,7 +4,7 @@ type
   TOption = enum
     optNone, optForceFullMake, optBoehmGC, optRefcGC, optRangeCheck,
     optBoundsCheck, optOverflowCheck, optNilCheck, optAssert, optLineDir,
-    optWarns, optHints, optDeadCodeElim, optListCmd, optCompileOnly,
+    optWarns, optHints, optListCmd, optCompileOnly,
     optSafeCode,             # only allow safe code
     optStyleCheck, optOptimizeSpeed, optOptimizeSize, optGenDynLib,
     optGenGuiApp, optStackTrace
diff --git a/tests/errmsgs/t1154.nim b/tests/errmsgs/t1154.nim
new file mode 100644
index 000000000..7fcbf8a27
--- /dev/null
+++ b/tests/errmsgs/t1154.nim
@@ -0,0 +1,11 @@
+discard """
+errormsg: "invalid type: 'expr' in this context: 'proc (a: varargs[expr])' for proc"
+line: 8
+"""
+
+import typetraits
+
+proc foo(a:varargs[expr]) =
+  echo a[0].type.name
+
+foo(1)
diff --git a/tests/errmsgs/tcant_overload_by_return_type.nim b/tests/errmsgs/tcant_overload_by_return_type.nim
new file mode 100644
index 000000000..613a896b4
--- /dev/null
+++ b/tests/errmsgs/tcant_overload_by_return_type.nim
@@ -0,0 +1,9 @@
+discard """
+errormsg: "overloaded 'x' leads to ambiguous calls"
+line: 9
+"""
+
+# bug #6393
+
+proc x(): int = 7
+proc x(): string = "strange"
diff --git a/tests/errmsgs/tinvalidinout.nim b/tests/errmsgs/tinvalidinout.nim
index ce7eb6022..1fa3805ee 100644
--- a/tests/errmsgs/tinvalidinout.nim
+++ b/tests/errmsgs/tinvalidinout.nim
@@ -1,10 +1,10 @@
 discard """
 cmd: "nim check $file"
-errormsg: "The `in` modifier can be used only with imported types"
+errormsg: "the 'in' modifier can be used only with imported types"
 nimout: '''
-tinvalidinout.nim(14, 7) Error: The `out` modifier can be used only with imported types
-tinvalidinout.nim(17, 9) Error: The `in` modifier can be used only with imported types
-tinvalidinout.nim(18, 9) Error: The `in` modifier can be used only with imported types
+tinvalidinout.nim(14, 7) Error: the 'out' modifier can be used only with imported types
+tinvalidinout.nim(17, 9) Error: the 'in' modifier can be used only with imported types
+tinvalidinout.nim(18, 9) Error: the 'in' modifier can be used only with imported types
 '''
 """
 
diff --git a/tests/errmsgs/tproper_stacktrace2.nim b/tests/errmsgs/tproper_stacktrace2.nim
index 5f312b870..44b208c87 100644
--- a/tests/errmsgs/tproper_stacktrace2.nim
+++ b/tests/errmsgs/tproper_stacktrace2.nim
@@ -3,7 +3,7 @@ discard """
   exitcode: 1
 """
 
-proc returnsNil(): string = return nil
+proc returnsNil(): ref int = return nil
 
 iterator fields*(a, b: int): int =
   if a == b:
@@ -17,6 +17,6 @@ proc main(): string =
   result = ""
   for i in fields(0, 1):
     let x = returnsNil()
-    result &= "string literal " & $x
+    result &= "string literal " & $x[]
 
 echo main()
diff --git a/tests/exprs/tstmtexprs.nim b/tests/exprs/tstmtexprs.nim
index 2a0ec2821..577f314ec 100644
--- a/tests/exprs/tstmtexprs.nim
+++ b/tests/exprs/tstmtexprs.nim
@@ -81,7 +81,7 @@ semiProblem()
 # bug #844
 
 import json
-proc parseResponse(): PJsonNode =
+proc parseResponse(): JsonNode =
   result = % { "key1": % { "key2": % "value" } }
   for key, val in result["key1"]:
     var excMsg = key & "("
diff --git a/tests/flags/tgenscript.nim b/tests/flags/tgenscript.nim
new file mode 100644
index 000000000..6a037b5d8
--- /dev/null
+++ b/tests/flags/tgenscript.nim
@@ -0,0 +1,5 @@
+discard """
+  file: "tgenscript.nim"
+"""
+
+echo "--genscript"
diff --git a/tests/generics/module_with_generics.nim b/tests/generics/module_with_generics.nim
new file mode 100644
index 000000000..e801a4790
--- /dev/null
+++ b/tests/generics/module_with_generics.nim
@@ -0,0 +1,14 @@
+type
+  Base[T] = ref object {.inheritable.}
+    value*: T
+
+  Derived[T] = ref object of Base[T]
+    derivedValue*: T
+
+proc makeDerived*[T](v: T): Derived[T] =
+  new result
+  result.value = v
+
+proc setBaseValue*[T](a: Base[T], value: T) =
+  a.value = value
+
diff --git a/tests/generics/t5602_inheritence.nim b/tests/generics/t5602_inheritence.nim
index 6d48c796e..ee5ba89d5 100644
--- a/tests/generics/t5602_inheritence.nim
+++ b/tests/generics/t5602_inheritence.nim
@@ -1,10 +1,11 @@
 discard """
   output: "seq[float]\n0"
+  targets: "c cpp"
 """
 
 # https://github.com/nim-lang/Nim/issues/5602
 
-import typetraits
+import typetraits, module_with_generics
 
 type
   Foo[T] = object of RootObj
@@ -16,3 +17,8 @@ proc p[T](f: Foo[T]): T =
 var s: Bar[float]
 echo p(s).len # the bug was: p(s) should return seq[float], but returns float instead
 
+# Test overloading and code generation when
+# downcasting is required for generic types:
+var d = makeDerived(10)
+setBaseValue(d, 20)
+
diff --git a/tests/iter/tobj_iter.nim b/tests/iter/tobj_iter.nim
index eb0e37b23..a894755d7 100644
--- a/tests/iter/tobj_iter.nim
+++ b/tests/iter/tobj_iter.nim
@@ -4,8 +4,6 @@ discard """
 
 # bug #2023
 
-{.deadCodeElim:on.}
-
 type
     Obj = object
         iter: iterator (): int8 {.closure.}
diff --git a/tests/lexer/tunderscores.nim b/tests/lexer/tunderscores.nim
index e718fb40a..f64f36977 100644
--- a/tests/lexer/tunderscores.nim
+++ b/tests/lexer/tunderscores.nim
@@ -1,7 +1,7 @@
 discard """
   file: "tunderscores.nim"
   line: 8
-  errormsg: "invalid token: _"
+  errormsg: "invalid token: trailing underscore"
 """
 # Bug #502670
 
diff --git a/tests/macros/tmacro1.nim b/tests/macros/tmacro1.nim
index 0b83345a1..ac2bf9094 100644
--- a/tests/macros/tmacro1.nim
+++ b/tests/macros/tmacro1.nim
@@ -27,11 +27,19 @@ import strutils
 template assertNot(arg: untyped): untyped =
   assert(not(arg))
 
+
+proc foo(arg: int): void =
+  discard
+
+proc foo(arg: float): void =
+  discard
+
 static:
   ## test eqIdent
   let a = "abc_def"
   let b = "abcDef"
   let c = "AbcDef"
+  let d = nnkBracketExpr.newTree() # not an identifier at all
 
   assert eqIdent(             a ,              b )
   assert eqIdent(newIdentNode(a),              b )
@@ -62,3 +70,12 @@ static:
   assertNot eqIdent(genSym(nskLet, c), newIdentNode(  b))
   assertNot eqIdent(newIdentNode(  c), genSym(nskLet, b))
   assertNot eqIdent(genSym(nskLet, c), genSym(nskLet, b))
+
+  # eqIdent on non identifier at all
+  assertNot eqIdent(a,d)
+
+  # eqIdent on sym choice
+  let fooSym = bindSym"foo"
+  assert fooSym.kind in {nnkOpenSymChoice, nnkClosedSymChoice}
+  assert    fooSym.eqIdent("fOO")
+  assertNot fooSym.eqIdent("bar")
diff --git a/tests/macros/tstructuredlogging.nim b/tests/macros/tstructuredlogging.nim
new file mode 100644
index 000000000..05bb52a40
--- /dev/null
+++ b/tests/macros/tstructuredlogging.nim
@@ -0,0 +1,154 @@
+discard """
+output: '''
+main started: a=10, b=inner-b, c=10, d=some-d, x=16, z=20
+exiting: a=12, b=overriden-b, c=100, msg=bye bye, x=16
+'''
+"""
+
+import macros, tables
+
+template scopeHolder =
+  0 # scope revision number
+
+type
+  BindingsSet = Table[string, NimNode]
+
+proc actualBody(n: NimNode): NimNode =
+  # skip over the double StmtList node introduced in `mergeScopes`
+  result = n.body
+  if result.kind == nnkStmtList and result[0].kind == nnkStmtList:
+    result = result[0]
+
+iterator bindings(n: NimNode, skip = 0): (string, NimNode) =
+  for i in skip ..< n.len:
+    let child = n[i]
+    if child.kind in {nnkAsgn, nnkExprEqExpr}:
+      let name = $child[0]
+      let value = child[1]
+      yield (name, value)
+
+proc scopeRevision(scopeHolder: NimNode): int =
+  # get the revision number from a scopeHolder sym
+  assert scopeHolder.kind == nnkSym
+  var revisionNode = scopeHolder.getImpl.actualBody[0]
+  result = int(revisionNode.intVal)
+
+proc lastScopeHolder(scopeHolders: NimNode): NimNode =
+  # get the most recent scopeHolder from a symChoice node
+  if scopeHolders.kind in {nnkClosedSymChoice, nnkOpenSymChoice}:
+    var bestScopeRev = 0
+    assert scopeHolders.len > 0
+    for scope in scopeHolders:
+      let rev = scope.scopeRevision
+      if result == nil or rev > bestScopeRev:
+        result = scope
+        bestScopeRev = rev
+  else:
+    result = scopeHolders
+
+  assert result.kind == nnkSym
+
+macro mergeScopes(scopeHolders: typed, newBindings: untyped): untyped =
+  var
+    bestScope = scopeHolders.lastScopeHolder
+    bestScopeRev = bestScope.scopeRevision
+
+  var finalBindings = initTable[string, NimNode]()
+  for k, v in bindings(bestScope.getImpl.actualBody, skip = 1):
+    finalBindings[k] = v
+
+  for k, v in bindings(newBindings):
+    finalBindings[k] = v
+
+  var newScopeDefinition = newStmtList(newLit(bestScopeRev + 1))
+
+  for k, v in finalBindings:
+    newScopeDefinition.add newAssignment(newIdentNode(k), v)
+
+  result = quote:
+    template scopeHolder = `newScopeDefinition`
+
+template scope(newBindings: untyped) {.dirty.} =
+  mergeScopes(bindSym"scopeHolder", newBindings)
+
+type
+  TextLogRecord = object
+    line: string
+
+  StdoutLogRecord = object
+
+template setProperty(r: var TextLogRecord, key: string, val: string, isFirst: bool) =
+  if not first: r.line.add ", "
+  r.line.add key
+  r.line.add "="
+  r.line.add val
+
+template setEventName(r: var StdoutLogRecord, name: string) =
+  stdout.write(name & ": ")
+
+template setProperty(r: var StdoutLogRecord, key: string, val: auto, isFirst: bool) =
+  when not isFirst: stdout.write ", "
+  stdout.write key
+  stdout.write "="
+  stdout.write $val
+
+template flushRecord(r: var StdoutLogRecord) =
+  stdout.write "\n"
+  stdout.flushFile
+
+macro logImpl(scopeHolders: typed,
+              logStmtProps: varargs[untyped]): untyped =
+  let lexicalScope = scopeHolders.lastScopeHolder.getImpl.actualBody
+  var finalBindings = initOrderedTable[string, NimNode]()
+
+  for k, v in bindings(lexicalScope, skip = 1):
+    finalBindings[k] = v
+
+  for k, v in bindings(logStmtProps, skip = 1):
+    finalBindings[k] = v
+
+  finalBindings.sort(system.cmp)
+
+  let eventName = logStmtProps[0]
+  assert eventName.kind in {nnkStrLit}
+  let record = genSym(nskVar, "record")
+
+  result = quote:
+    var `record`: StdoutLogRecord
+    setEventName(`record`, `eventName`)
+
+  var isFirst = true
+  for k, v in finalBindings:
+    result.add newCall(newIdentNode"setProperty",
+                       record, newLit(k), v, newLit(isFirst))
+    isFirst = false
+
+  result.add newCall(newIdentNode"flushRecord", record)
+
+template log(props: varargs[untyped]) {.dirty.} =
+  logImpl(bindSym"scopeHolder", props)
+
+scope:
+  a = 12
+  b = "original-b"
+
+scope:
+  x = 16
+  b = "overriden-b"
+
+scope:
+  c = 100
+
+proc main =
+  scope:
+    c = 10
+
+  scope:
+    z = 20
+
+  log("main started", a = 10, b = "inner-b", d = "some-d")
+
+main()
+
+log("exiting", msg = "bye bye")
+
diff --git a/tests/macros/ttemplatesymbols.nim b/tests/macros/ttemplatesymbols.nim
new file mode 100644
index 000000000..8d9c9ec02
--- /dev/null
+++ b/tests/macros/ttemplatesymbols.nim
@@ -0,0 +1,173 @@
+import
+  macros, algorithm, strutils
+
+proc normalProc(x: int) =
+  echo x
+
+template templateWithtouParams =
+  echo 10
+
+proc overloadedProc(x: int) =
+  echo x
+
+proc overloadedProc(x: string) =
+  echo x
+
+proc overloadedProc[T](x: T) =
+  echo x
+
+template normalTemplate(x: int) =
+  echo x
+
+template overloadedTemplate(x: int) =
+  echo x
+
+template overloadedTemplate(x: string) =
+  echo x
+
+macro normalMacro(x: int): untyped =
+  discard
+
+macro macroWithoutParams: untyped =
+  discard
+
+macro inspectSymbol(sym: typed, expected: static[string]): untyped =
+  if sym.kind == nnkSym:
+    echo "Symbol node:"
+    let res = sym.getImpl.repr & "\n"
+    echo res
+    # echo "|", res, "|"
+    # echo "|", expected, "|"
+    if expected.len > 0: assert res == expected
+  elif sym.kind in {nnkClosedSymChoice, nnkOpenSymChoice}:
+    echo "Begin sym choice:"
+    var results = newSeq[string](0)
+    for innerSym in sym:
+      results.add innerSym.getImpl.repr
+    sort(results, cmp[string])
+    let res = results.join("\n") & "\n"
+    echo res
+    if expected.len > 0: assert res == expected
+    echo "End symchoice."
+  else:
+    echo "Non-symbol node: ", sym.kind
+    if expected.len > 0: assert $sym.kind == expected
+
+macro inspectUntyped(sym: untyped, expected: static[string]): untyped =
+  let res = sym.repr
+  echo "Untyped node: ", res
+  assert res == expected
+
+inspectSymbol templateWithtouParams, "nnkCommand"
+  # this template is expanded, because bindSym was not used
+  # the end result is the template body (nnkCommand)
+
+inspectSymbol bindSym("templateWithtouParams"), """
+template templateWithtouParams() =
+  echo 10
+
+"""
+
+inspectSymbol macroWithoutParams, "nnkEmpty"
+  # Just like the template above, the macro was expanded
+
+inspectSymbol bindSym("macroWithoutParams"), """
+macro macroWithoutParams(): untyped =
+  discard
+
+"""
+
+inspectSymbol normalMacro, """
+macro normalMacro(x: int): untyped =
+  discard
+
+"""
+  # Since the normalMacro has params, it's automatically
+  # treated as a symbol here (no need for `bindSym`)
+
+inspectSymbol bindSym("normalMacro"), """
+macro normalMacro(x: int): untyped =
+  discard
+
+"""
+
+inspectSymbol normalTemplate, """
+template normalTemplate(x: int) =
+  echo x
+
+"""
+
+inspectSymbol bindSym("normalTemplate"), """
+template normalTemplate(x: int) =
+  echo x
+
+"""
+
+inspectSymbol overloadedTemplate, """
+template overloadedTemplate(x: int) =
+  echo x
+
+template overloadedTemplate(x: string) =
+  echo x
+
+"""
+
+inspectSymbol bindSym("overloadedTemplate"), """
+template overloadedTemplate(x: int) =
+  echo x
+
+template overloadedTemplate(x: string) =
+  echo x
+
+"""
+
+inspectUntyped bindSym("overloadedTemplate"), """bindSym("overloadedTemplate")"""
+  # binSym is active only in the presense of `typed` params.
+  # `untyped` params still get the raw AST
+
+inspectSymbol normalProc, """
+proc normalProc(x: int) =
+  echo [x]
+
+"""
+
+inspectSymbol bindSym("normalProc"), """
+proc normalProc(x: int) =
+  echo [x]
+
+"""
+
+inspectSymbol overloadedProc, """
+proc overloadedProc(x: int) =
+  echo [x]
+
+proc overloadedProc(x: string) =
+  echo [x]
+
+proc overloadedProc[T](x: T) =
+  echo x
+
+"""
+  # XXX: There seems to be a repr rendering problem above.
+  # Notice that `echo [x]`
+
+inspectSymbol overloadedProc[float], """
+proc overloadedProc(x: T) =
+  echo [x]
+
+"""
+  # As expected, when we select a specific generic, the
+  # AST is no longer a symChoice
+
+inspectSymbol bindSym("overloadedProc"), """
+proc overloadedProc(x: int) =
+  echo [x]
+
+proc overloadedProc(x: string) =
+  echo [x]
+
+proc overloadedProc[T](x: T) =
+  echo x
+
+"""
+
diff --git a/tests/manyloc/argument_parser/argument_parser.nim b/tests/manyloc/argument_parser/argument_parser.nim
index 14352066d..1095a893e 100644
--- a/tests/manyloc/argument_parser/argument_parser.nim
+++ b/tests/manyloc/argument_parser/argument_parser.nim
@@ -209,7 +209,7 @@ proc `$`*(data: Tcommandline_results): string =
 
 # - Parse code
 
-template raise_or_quit(exception, message: expr): stmt {.immediate.} =
+template raise_or_quit(exception, message: untyped) =
   ## Avoids repeating if check based on the default quit_on_failure variable.
   ##
   ## As a special case, if message has a zero length the call to quit won't
@@ -230,15 +230,15 @@ template run_custom_proc(parsed_parameter: Tparsed_parameter,
   ##
   ## Pass in the string of the parameter triggering the call. If the
   if not custom_validator.isNil:
+    try:
+      let message = custom_validator(parameter, parsed_parameter)
+      if not message.isNil and message.len > 0:
+        raise_or_quit(ValueError, ("Failed to validate value for " &
+          "parameter $1:\n$2" % [escape(parameter), message]))
     except:
       raise_or_quit(ValueError, ("Couldn't run custom proc for " &
         "parameter $1:\n$2" % [escape(parameter),
         getCurrentExceptionMsg()]))
-    let message = custom_validator(parameter, parsed_parameter)
-    if not message.isNil and message.len > 0:
-      raise_or_quit(ValueError, ("Failed to validate value for " &
-        "parameter $1:\n$2" % [escape(parameter), message]))
-
 
 proc parse_parameter(quit_on_failure: bool, param, value: string,
     param_kind: Tparam_kind): Tparsed_parameter =
diff --git a/tests/manyloc/keineschweine/dependencies/chipmunk/chipmunk.nim b/tests/manyloc/keineschweine/dependencies/chipmunk/chipmunk.nim
index 56d3edec4..ac425c7a0 100644
--- a/tests/manyloc/keineschweine/dependencies/chipmunk/chipmunk.nim
+++ b/tests/manyloc/keineschweine/dependencies/chipmunk/chipmunk.nim
@@ -23,7 +23,6 @@ const Lib = "libchipmunk.so.6.1.1"
 
 when defined(MoreNim):
   {.hint: "MoreNim defined; some Chipmunk functions replaced in Nim".}
-{.deadCodeElim: on.}
 from math import sqrt, sin, cos, arctan2
 when defined(CpUseFloat):
   {.hint: "CpUseFloat defined; using float32 as float".}
@@ -389,13 +388,13 @@ type
     cdecl.}
 
 ##cp property emulators
-template defGetter(otype: typedesc, memberType: typedesc, memberName: expr, procName: expr): stmt {.immediate.} =
+template defGetter(otype: typedesc, memberType: typedesc, memberName, procName: untyped) =
   proc `get procName`*(obj: otype): memberType {.cdecl.} =
     return obj.memberName
-template defSetter(otype: typedesc, memberType: typedesc, memberName: expr, procName: expr): stmt {.immediate.} =
+template defSetter(otype: typedesc, memberType: typedesc, memberName, procName: untyped) =
   proc `set procName`*(obj: otype, value: memberType) {.cdecl.} =
     obj.memberName = value
-template defProp(otype: typedesc, memberType: typedesc, memberName: expr, procName: expr): stmt {.immediate.} =
+template defProp(otype: typedesc, memberType: typedesc, memberName, procName: untyped) =
   defGetter(otype, memberType, memberName, procName)
   defSetter(otype, memberType, memberName, procName)
 
@@ -909,7 +908,7 @@ proc getShapes*(arb: PArbiter, a, b: var PShape) {.inline.} =
 
 #/ A macro shortcut for defining and retrieving the shapes from an arbiter.
 #define CP_ARBITER_GET_SHAPES(arb, a, b) cpShape *a, *b; cpArbiterGetShapes(arb, &a, &b);
-template getShapes*(arb: PArbiter, name1, name2: expr): stmt {.immediate.} =
+template getShapes*(arb: PArbiter, name1, name2: untyped) =
   var name1, name2: PShape
   getShapes(arb, name1, name2)
 
@@ -924,7 +923,7 @@ template getShapes*(arb: PArbiter, name1, name2: expr): stmt {.immediate.} =
 
 #/ A macro shortcut for defining and retrieving the bodies from an arbiter.
 #define CP_ARBITER_GET_BODIES(arb, a, b) cpBody *a, *b; cpArbiterGetBodies(arb, &a, &b);
-template getBodies*(arb: PArbiter, name1, name2: expr): stmt {.immediate.} =
+template getBodies*(arb: PArbiter, name1, name2: untyped) =
   var name1, name2: PBOdy
   getBodies(arb, name1, name2)
 
@@ -948,11 +947,11 @@ proc getDepth*(arb: PArbiter; i: cint): CpFloat {.
   cdecl, importc: "cpArbiterGetDepth", dynlib: Lib.}
 
 ##Shapes
-template defShapeSetter(memberType: typedesc, memberName: expr, procName: expr, activates: bool): stmt {.immediate.} =
+template defShapeSetter(memberType: typedesc, memberName: untyped, procName: untyped, activates: bool) =
   proc `set procName`*(obj: PShape, value: memberType) {.cdecl.} =
     if activates and obj.body != nil: obj.body.activate()
     obj.memberName = value
-template defShapeProp(memberType: typedesc, memberName: expr, procName: expr, activates: bool): stmt {.immediate.} =
+template defShapeProp(memberType: typedesc, memberName: untyped, procName: untyped, activates: bool) =
   defGetter(PShape, memberType, memberName, procName)
   defShapeSetter(memberType, memberName, procName, activates)
 
@@ -1273,11 +1272,11 @@ proc activateBodies(constraint: PConstraint) {.inline.} =
 # 	cpConstraintActivateBodies(constraint); \
 # 	constraint->member = value; \
 # }
-template defConstraintSetter(memberType: typedesc, member: expr, name: expr): stmt {.immediate.} =
+template defConstraintSetter(memberType: typedesc, member, name: untyped) =
   proc `set name`*(constraint: PConstraint, value: memberType) {.cdecl.} =
     activateBodies(constraint)
     constraint.member = value
-template defConstraintProp(memberType: typedesc, member: expr, name: expr): stmt {.immediate.} =
+template defConstraintProp(memberType: typedesc, member, name: untyped) =
   defGetter(PConstraint, memberType, member, name)
   defConstraintSetter(memberType, member, name)
 # CP_DefineConstraintStructGetter(cpSpace*, CP_PRIVATE(space), Space)
@@ -1307,18 +1306,18 @@ proc getImpulse*(constraint: PConstraint): CpFloat {.inline.} =
 # 	cpConstraintActivateBodies(constraint); \
 # 	((struct *)constraint)->member = value; \
 # }
-template constraintCheckCast(constraint: PConstraint, ctype: expr): stmt {.immediate.} =
+template constraintCheckCast(constraint: PConstraint, ctype: untyped) =
   assert(constraint.klass == `ctype getClass`(), "Constraint is the wrong class")
-template defCGetter(ctype: expr, memberType: typedesc, member: expr, name: expr): stmt {.immediate.} =
+template defCGetter(ctype: untyped, memberType: typedesc, member, name: untyped) =
   proc `get ctype name`*(constraint: PConstraint): memberType {.cdecl.} =
     constraintCheckCast(constraint, ctype)
     result = cast[`P ctype`](constraint).member
-template defCSetter(ctype: expr, memberType: typedesc, member: expr, name: expr): stmt {.immediate.} =
+template defCSetter(ctype: untyped, memberType: typedesc, member, name: untyped) =
   proc `set ctype name`*(constraint: PConstraint, value: memberType) {.cdecl.} =
     constraintCheckCast(constraint, ctype)
     activateBodies(constraint)
     cast[`P ctype`](constraint).member = value
-template defCProp(ctype: expr, memberType: typedesc, member: expr, name: expr): stmt {.immediate.} =
+template defCProp(ctype: untyped, memberType: typedesc, member, name: untyped) =
   defCGetter(ctype, memberType, member, name)
   defCSetter(ctype, memberType, member, name)
 
diff --git a/tests/manyloc/keineschweine/dependencies/enet/enet.nim b/tests/manyloc/keineschweine/dependencies/enet/enet.nim
index 3c4ce2017..07079f2ea 100644
--- a/tests/manyloc/keineschweine/dependencies/enet/enet.nim
+++ b/tests/manyloc/keineschweine/dependencies/enet/enet.nim
@@ -20,12 +20,11 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
 const Lib = "libenet.so.1(|.0.3)"
 
-{.deadCodeElim: on.}
 const
   ENET_VERSION_MAJOR* = 1
   ENET_VERSION_MINOR* = 3
   ENET_VERSION_PATCH* = 3
-template ENET_VERSION_CREATE(major, minor, patch: expr): expr =
+template ENET_VERSION_CREATE(major, minor, patch: untyped): untyped =
   (((major) shl 16) or ((minor) shl 8) or (patch))
 
 const
@@ -278,22 +277,22 @@ when defined(Linux) or true:
       dataLength*: csize
     TENetSocketSet* = Tfd_set
   ## see if these are different on win32, if not then get rid of these
-  template ENET_HOST_TO_NET_16*(value: expr): expr =
+  template ENET_HOST_TO_NET_16*(value: untyped): untyped =
     (htons(value))
-  template ENET_HOST_TO_NET_32*(value: expr): expr =
+  template ENET_HOST_TO_NET_32*(value: untyped): untyped =
     (htonl(value))
-  template ENET_NET_TO_HOST_16*(value: expr): expr =
+  template ENET_NET_TO_HOST_16*(value: untyped): untyped =
     (ntohs(value))
-  template ENET_NET_TO_HOST_32*(value: expr): expr =
+  template ENET_NET_TO_HOST_32*(value: untyped): untyped =
     (ntohl(value))
 
-  template ENET_SOCKETSET_EMPTY*(sockset: expr): expr =
+  template ENET_SOCKETSET_EMPTY*(sockset: untyped): untyped =
     FD_ZERO(addr((sockset)))
-  template ENET_SOCKETSET_ADD*(sockset, socket: expr): expr =
+  template ENET_SOCKETSET_ADD*(sockset, socket: untyped): untyped =
     FD_SET(socket, addr((sockset)))
-  template ENET_SOCKETSET_REMOVE*(sockset, socket: expr): expr =
+  template ENET_SOCKETSET_REMOVE*(sockset, socket: untyped): untyped =
     FD_CLEAR(socket, addr((sockset)))
-  template ENET_SOCKETSET_CHECK*(sockset, socket: expr): expr =
+  template ENET_SOCKETSET_CHECK*(sockset, socket: untyped): untyped =
     FD_ISSET(socket, addr((sockset)))
 
 when defined(Windows):
@@ -607,7 +606,7 @@ proc protocolCommandSize*(commandNumber: cuchar): csize{.
 
 {.pop.}
 
-from hashes import `!$`, `!&`, THash, hash
-proc hash*(x: TAddress): THash {.nimcall, noSideEffect.} =
+from hashes import `!$`, `!&`, Hash, hash
+proc hash*(x: TAddress): Hash {.nimcall, noSideEffect.} =
   result = !$(hash(x.host.int32) !& hash(x.port.int16))
 
diff --git a/tests/manyloc/keineschweine/dependencies/genpacket/genpacket_enet.nim b/tests/manyloc/keineschweine/dependencies/genpacket/genpacket_enet.nim
index 142b190ab..3fb4dc7d9 100644
--- a/tests/manyloc/keineschweine/dependencies/genpacket/genpacket_enet.nim
+++ b/tests/manyloc/keineschweine/dependencies/genpacket/genpacket_enet.nim
@@ -1,15 +1,15 @@
 import macros, macro_dsl, estreams
 from strutils import format
 
-template newLenName(): stmt {.immediate.} =
+template newLenName() =
   let lenName {.inject.} = ^("len"& $lenNames)
   inc(lenNames)
 
-template defPacketImports*(): stmt {.immediate, dirty.} =
+template defPacketImports*() {.dirty.} =
   import macros, macro_dsl, estreams
   from strutils import format
 
-macro defPacket*(typeNameN: expr, typeFields: expr): stmt {.immediate.} =
+macro defPacket*(typeNameN: untyped, typeFields: untyped): untyped =
   result = newNimNode(nnkStmtList)
   let
     typeName = quoted2ident(typeNameN)
@@ -60,7 +60,7 @@ macro defPacket*(typeNameN: expr, typeFields: expr): stmt {.immediate.} =
     let
       name = typeFields[i][0]
       dotName = packetID.dot(name)
-      resName = newIdentNode(!"result").dot(name)
+      resName = newIdentNode("result").dot(name)
     case typeFields[i][1].kind
     of nnkBracketExpr: #ex: paddedstring[32, '\0'], array[range, type]
       case $typeFields[i][1][0].ident
@@ -141,7 +141,7 @@ macro defPacket*(typeNameN: expr, typeFields: expr): stmt {.immediate.} =
 
   const emptyFields = {nnkEmpty, nnkNilLit}
   var objFields = newNimNode(nnkRecList)
-  for i in 0.. < len(typeFields):
+  for i in 0 ..< len(typeFields):
     let fname = typeFields[i][0]
     constructorParams.add(newNimNode(nnkIdentDefs).und(
       fname,
@@ -200,7 +200,7 @@ proc iddefs*(a: string; b: NimNode): NimNode {.compileTime.} =
 proc varTy*(a: NimNode): NimNode {.compileTime.} =
   result = newNimNode(nnkVarTy).und(a)
 
-macro forwardPacket*(typeName: expr, underlyingType: expr): stmt {.immediate.} =
+macro forwardPacket*(typeName: untyped, underlyingType: untyped): untyped =
   var
     packetID = ^"p"
     streamID = ^"s"
@@ -234,7 +234,7 @@ macro forwardPacket*(typeName: expr, underlyingType: expr): stmt {.immediate.} =
     echo "unknown type:", repr(underlyingtype)
   echo(repr(result))
 
-template forwardPacketT*(typeName: expr; underlyingType: expr): stmt {.dirty, immediate.} =
+template forwardPacketT*(typeName: untyped; underlyingType: untyped) {.dirty.} =
   proc `read typeName`*(buffer: PBuffer): typeName =
     #discard readData(s, addr result, sizeof(result))
     var res: underlyingType
diff --git a/tests/manyloc/keineschweine/dependencies/genpacket/macro_dsl.nim b/tests/manyloc/keineschweine/dependencies/genpacket/macro_dsl.nim
index d3a0c701d..33d2a7177 100644
--- a/tests/manyloc/keineschweine/dependencies/genpacket/macro_dsl.nim
+++ b/tests/manyloc/keineschweine/dependencies/genpacket/macro_dsl.nim
@@ -1,5 +1,5 @@
 import macros
-{.deadCodeElim: on.}
+
 #Inline macro.add() to allow for easier nesting
 proc und*(a: NimNode; b: NimNode): NimNode {.compileTime.} =
   a.add(b)
@@ -10,7 +10,7 @@ proc und*(a: NimNode; b: varargs[NimNode]): NimNode {.compileTime.} =
 
 proc `^`*(a: string): NimNode {.compileTime.} =
   ## new ident node
-  result = newIdentNode(!a)
+  result = newIdentNode(a)
 proc `[]`*(a, b: NimNode): NimNode {.compileTime.} =
   ## new bracket expression: node[node] not to be confused with node[indx]
   result = newNimNode(nnkBracketExpr).und(a, b)
@@ -34,7 +34,7 @@ proc emptyNode*(): NimNode {.compileTime.} =
 proc dot*(left, right: NimNode): NimNode {.compileTime.} =
   result = newNimNode(nnkDotExpr).und(left, right)
 proc prefix*(a: string, b: NimNode): NimNode {.compileTime.} =
-  result = newNimNode(nnkPrefix).und(newIdentNode(!a), b)
+  result = newNimNode(nnkPrefix).und(newIdentNode(a), b)
 
 proc quoted2ident*(a: NimNode): NimNode {.compileTime.} =
   if a.kind != nnkAccQuoted:
@@ -45,13 +45,13 @@ proc quoted2ident*(a: NimNode): NimNode {.compileTime.} =
   result = ^pname
 
 
-macro `?`(a: expr): expr =
+macro `?`(a: untyped): untyped =
   ## Character literal ?A #=> 'A'
   result = ($a[1].ident)[0].lit
 ## echo(?F,?a,?t,?t,?y)
 
 when isMainModule:
-  macro foo(x: stmt): stmt =
+  macro foo(x: untyped) =
     result = newNimNode(nnkStmtList)
     result.add(newNimNode(nnkCall).und(!!"echo", "Hello thar".lit))
     result.add(newCall("echo", lit("3 * 45 = "), (3.lit.infix("*", 45.lit))))
diff --git a/tests/manyloc/keineschweine/dependencies/nake/nakefile.nim b/tests/manyloc/keineschweine/dependencies/nake/nakefile.nim
index bdf2139c9..5490211de 100644
--- a/tests/manyloc/keineschweine/dependencies/nake/nakefile.nim
+++ b/tests/manyloc/keineschweine/dependencies/nake/nakefile.nim
@@ -7,7 +7,7 @@ task "install", "compile and install nake binary":
     for index, dir in pairs(path):
       echo "  ", index, ". ", dir
     echo "Where to install nake binary? (quit with ^C or quit or exit)"
-    let ans = stdin.readLine().toLower
+    let ans = stdin.readLine().toLowerAscii
     var index = 0
     case ans
     of "q", "quit", "x", "exit":
diff --git a/tests/manyloc/keineschweine/dependencies/sfml/sfml.nim b/tests/manyloc/keineschweine/dependencies/sfml/sfml.nim
index 1524f0eb4..0060bf12b 100644
--- a/tests/manyloc/keineschweine/dependencies/sfml/sfml.nim
+++ b/tests/manyloc/keineschweine/dependencies/sfml/sfml.nim
@@ -12,7 +12,7 @@ else:
     LibS = "libcsfml-system.so.2.0"
     LibW = "libcsfml-window.so.2.0"
   #{.error: "Platform unsupported".}
-{.deadCodeElim: on.}
+
 {.pragma: pf, pure, final.}
 type
   PClock* = ptr TClock
diff --git a/tests/manyloc/keineschweine/dependencies/sfml/sfml_colors.nim b/tests/manyloc/keineschweine/dependencies/sfml/sfml_colors.nim
index 95a760e1f..b4eb1b8f0 100644
--- a/tests/manyloc/keineschweine/dependencies/sfml/sfml_colors.nim
+++ b/tests/manyloc/keineschweine/dependencies/sfml/sfml_colors.nim
@@ -1,5 +1,5 @@
 import sfml
-{.deadCodeElim: on.}
+
 let
   Black*: TColor = color(0, 0, 0)
   White*: TColor = color(255, 255, 255)
diff --git a/tests/manyloc/keineschweine/dependencies/sfml/sfml_vector.nim b/tests/manyloc/keineschweine/dependencies/sfml/sfml_vector.nim
index 474d249aa..94c5308a9 100644
--- a/tests/manyloc/keineschweine/dependencies/sfml/sfml_vector.nim
+++ b/tests/manyloc/keineschweine/dependencies/sfml/sfml_vector.nim
@@ -1,2 +1 @@
 import sfml, math, strutils
-{.deadCodeElim: on.}
diff --git a/tests/manyloc/keineschweine/keineschweine.nim b/tests/manyloc/keineschweine/keineschweine.nim
index c0f1bc031..59347ee46 100644
--- a/tests/manyloc/keineschweine/keineschweine.nim
+++ b/tests/manyloc/keineschweine/keineschweine.nim
@@ -5,7 +5,7 @@ import
   sg_gui, sg_assets, sound_buffer, enet_client
 when defined(profiler):
   import nimprof
-{.deadCodeElim: on.}
+
 type
   PPlayer* = ref TPlayer
   TPlayer* = object
diff --git a/tests/manyloc/keineschweine/lib/estreams.nim b/tests/manyloc/keineschweine/lib/estreams.nim
index bdf9b2bf0..00a55c626 100644
--- a/tests/manyloc/keineschweine/lib/estreams.nim
+++ b/tests/manyloc/keineschweine/lib/estreams.nim
@@ -78,7 +78,7 @@ proc write*(buffer: PBuffer; val: var string) =
   setLen buffer.data, buffer.pos + length.int
   copyMem(addr buffer.data[buffer.pos], addr val[0], length.int)
   inc buffer.pos, length.int
-proc write*[T: TNumber|bool|char|byte](buffer: PBuffer; val: T) =
+proc write*[T: SomeNumber|bool|char|byte](buffer: PBuffer; val: T) =
   var v: T
   shallowCopy v, val
   writeBE buffer, v
diff --git a/tests/manyloc/keineschweine/lib/input_helpers.nim b/tests/manyloc/keineschweine/lib/input_helpers.nim
index 1953cb58c..ff1286c8d 100644
--- a/tests/manyloc/keineschweine/lib/input_helpers.nim
+++ b/tests/manyloc/keineschweine/lib/input_helpers.nim
@@ -5,8 +5,8 @@ type
   TInputFinishedProc* = proc()
   TKeyCallback = proc()
   PKeyClient* = ref object
-    onKeyDown: TTable[int32, TKeyCallback]
-    onKeyUp: TTable[int32, TKeyCallback]
+    onKeyDown: Table[int32, TKeyCallback]
+    onKeyUp: Table[int32, TKeyCallback]
     name: string
   PTextInput* = ref object
     text*: string
@@ -134,5 +134,5 @@ iterator pollEvents*(window: PRenderWindow): PEvent =
     of EvtMouseButtonReleased: addButtonEvent(event.mouseButton.button, up)
     of EvtTextEntered: recordText(activeInput, event.text)
     of EvtMouseMoved: setMousePos(event.mouseMove.x, event.mouseMove.y)
-    else: nil
+    else: discard
     yield(addr event)
diff --git a/tests/manyloc/keineschweine/lib/sg_assets.nim b/tests/manyloc/keineschweine/lib/sg_assets.nim
index fbc3c9ab8..1e8a99c83 100644
--- a/tests/manyloc/keineschweine/lib/sg_assets.nim
+++ b/tests/manyloc/keineschweine/lib/sg_assets.nim
@@ -59,7 +59,7 @@ type
     of Projectile:
       bullet*: PBulletRecord
     else:
-      nil
+      discard
   PBulletRecord* = ref TBulletRecord
   TBulletRecord* = object
     id*: int16
@@ -115,10 +115,10 @@ var
   cfg: PZoneSettings
   SpriteSheets* = initTable[string, PSpriteSheet](64)
   SoundCache  * = initTable[string, PSoundRecord](64)
-  nameToVehID*: TTable[string, int]
-  nameToItemID*: TTable[string, int]
-  nameToObjID*: TTable[string, int]
-  nameToBulletID*: TTable[string, int]
+  nameToVehID*: Table[string, int]
+  nameToItemID*: Table[string, int]
+  nameToObjID*: Table[string, int]
+  nameToBulletID*: Table[string, int]
   activeState = Lobby
 
 proc newSprite*(filename: string; errors: var seq[string]): PSpriteSheet
@@ -126,7 +126,7 @@ proc load*(ss: PSpriteSheet): bool {.discardable.}
 proc newSound*(filename: string; errors: var seq[string]): PSoundRecord
 proc load*(s: PSoundRecord): bool {.discardable.}
 
-proc validateSettings*(settings: PJsonNode; errors: var seq[string]): bool
+proc validateSettings*(settings: JsonNode; errors: var seq[string]): bool
 proc loadSettings*(rawJson: string, errors: var seq[string]): bool
 proc loadSettingsFromFile*(filename: string, errors: var seq[string]): bool
 
@@ -135,17 +135,17 @@ proc fetchItm*(itm: string): PItemRecord
 proc fetchObj*(name: string): PObjectRecord
 proc fetchBullet(name: string): PBulletRecord
 
-proc importLevel(data: PJsonNode; errors: var seq[string]): PLevelSettings
-proc importVeh(data: PJsonNode; errors: var seq[string]): PVehicleRecord
-proc importObject(data: PJsonNode; errors: var seq[string]): PObjectRecord
-proc importItem(data: PJsonNode; errors: var seq[string]): PItemRecord
-proc importPhys(data: PJsonNode): TPhysicsRecord
-proc importAnim(data: PJsonNode; errors: var seq[string]): PAnimationRecord
-proc importHandling(data: PJsonNode): THandlingRecord
-proc importBullet(data: PJsonNode; errors: var seq[string]): PBulletRecord
-proc importSoul(data: PJsonNode): TSoulRecord
-proc importExplosion(data: PJsonNode; errors: var seq[string]): TExplosionRecord
-proc importSound*(data: PJsonNode; errors: var seq[string]; fieldName: string = nil): PSoundRecord
+proc importLevel(data: JsonNode; errors: var seq[string]): PLevelSettings
+proc importVeh(data: JsonNode; errors: var seq[string]): PVehicleRecord
+proc importObject(data: JsonNode; errors: var seq[string]): PObjectRecord
+proc importItem(data: JsonNode; errors: var seq[string]): PItemRecord
+proc importPhys(data: JsonNode): TPhysicsRecord
+proc importAnim(data: JsonNode; errors: var seq[string]): PAnimationRecord
+proc importHandling(data: JsonNode): THandlingRecord
+proc importBullet(data: JsonNode; errors: var seq[string]): PBulletRecord
+proc importSoul(data: JsonNode): TSoulRecord
+proc importExplosion(data: JsonNode; errors: var seq[string]): TExplosionRecord
+proc importSound*(data: JsonNode; errors: var seq[string]; fieldName: string = nil): PSoundRecord
 
 ## this is the only pipe between lobby and main.nim
 proc getActiveState*(): TGameState =
@@ -203,7 +203,7 @@ iterator playableVehicles*(): PVehicleRecord =
     if v.playable:
       yield v
 
-template allAssets*(body: stmt) {.dirty.}=
+template allAssets*(body: untyped) {.dirty.}=
   block:
     var assetType = FGraphics
     for file, asset in pairs(SpriteSheets):
@@ -212,7 +212,7 @@ template allAssets*(body: stmt) {.dirty.}=
     for file, asset in pairs(SoundCache):
       body
 
-template cacheImpl(procName, cacheName, resultType: expr; body: stmt) {.dirty, immediate.} =
+template cacheImpl(procName, cacheName, resultType, body: untyped) {.dirty.} =
   proc procName*(filename: string; errors: var seq[string]): resulttype =
     if hasKey(cacheName, filename):
       return cacheName[filename]
@@ -220,7 +220,7 @@ template cacheImpl(procName, cacheName, resultType: expr; body: stmt) {.dirty, i
     body
     cacheName[filename] = result
 
-template checkFile(path: expr): stmt {.dirty, immediate.} =
+template checkFile(path: untyped) {.dirty.} =
   if not existsFile(path):
     errors.add("File missing: "& path)
 
@@ -243,7 +243,7 @@ proc expandPath*(assetType: TAssetType; fileName: string): string =
   case assetType
   of FGraphics: result.add "gfx/"
   of FSound:    result.add "sfx/"
-  else: nil
+  else: discard
   result.add fileName
 proc expandPath*(fc: ScFileChallenge): string {.inline.} =
   result = expandPath(fc.assetType, fc.file)
@@ -280,10 +280,10 @@ else:
     if not s.soundBuf.isNil:
       result = true
 
-template addError(e: expr): stmt {.immediate.} =
+template addError(e: untyped) =
   errors.add(e)
   result = false
-proc validateSettings*(settings: PJsonNode, errors: var seq[string]): bool =
+proc validateSettings*(settings: JsonNode, errors: var seq[string]): bool =
   result = true
   if settings.kind != JObject:
     addError("Settings root must be an object")
@@ -328,10 +328,10 @@ proc loadSettingsFromFile*(filename: string, errors: var seq[string]): bool =
     result = loadSettings(readFile(filename), errors)
 
 proc loadSettings*(rawJson: string, errors: var seq[string]): bool =
-  var settings: PJsonNode
+  var settings: JsonNode
   try:
     settings = parseJson(rawJson)
-  except EJsonParsingError:
+  except JsonParsingError:
     errors.add("JSON parsing error: "& getCurrentExceptionMsg())
     return
   except:
@@ -407,21 +407,21 @@ proc fetchObj*(name: string): PObjectRecord =
 proc fetchBullet(name: string): PBulletRecord =
   return cfg.bullets[nameToBulletID[name]]
 
-proc getField(node: PJsonNode, field: string, target: var float) =
+proc getField(node: JsonNode, field: string, target: var float) =
   if not node.hasKey(field):
     return
   if node[field].kind == JFloat:
     target = node[field].fnum
   elif node[field].kind == JInt:
     target = node[field].num.float
-proc getField(node: PJsonNode, field: string, target: var int) =
+proc getField(node: JsonNode, field: string, target: var int) =
   if not node.hasKey(field):
     return
   if node[field].kind == JInt:
     target = node[field].num.int
   elif node[field].kind == JFloat:
     target = node[field].fnum.int
-proc getField(node: PJsonNode; field: string; target: var bool) =
+proc getField(node: JsonNode; field: string; target: var bool) =
   if not node.hasKey(field):
     return
   case node[field].kind
@@ -431,19 +431,19 @@ proc getField(node: PJsonNode; field: string; target: var bool) =
     target = (node[field].num != 0)
   of JFloat:
     target = (node[field].fnum != 0.0)
-  else: nil
+  else: discard
 
-template checkKey(node: expr; key: string): stmt =
+template checkKey(node: untyped; key: string) =
   if not hasKey(node, key):
     return
 
-proc importTrail(data: PJsonNode; errors: var seq[string]): TTrailRecord =
+proc importTrail(data: JsonNode; errors: var seq[string]): TTrailRecord =
   checkKey(data, "trail")
   result.anim = importAnim(data["trail"], errors)
   result.timer = 1000.0
   getField(data["trail"], "timer", result.timer)
   result.timer /= 1000.0
-proc importLevel(data: PJsonNode; errors: var seq[string]): PLevelSettings =
+proc importLevel(data: JsonNode; errors: var seq[string]): PLevelSettings =
   new(result)
   result.size = vec2i(5000, 5000)
   result.starfield = @[]
@@ -456,7 +456,7 @@ proc importLevel(data: PJsonNode; errors: var seq[string]): PLevelSettings =
   if level.hasKey("starfield"):
     for star in level["starfield"].items:
       result.starfield.add(newSprite(star.str, errors))
-proc importPhys(data: PJsonNode): TPhysicsRecord =
+proc importPhys(data: JsonNode): TPhysicsRecord =
   result.radius = 20.0
   result.mass = 10.0
 
@@ -466,7 +466,7 @@ proc importPhys(data: PJsonNode): TPhysicsRecord =
     phys.getField("mass", result.mass)
   when not defined(NoChipmunk):
     result.moment = momentForCircle(result.mass, 0.0, result.radius, VectorZero) * MomentMult
-proc importHandling(data: PJsonNode): THandlingRecord =
+proc importHandling(data: JsonNode): THandlingRecord =
   result.thrust = 45.0
   result.topSpeed = 100.0 #unused
   result.reverse = 30.0
@@ -483,7 +483,7 @@ proc importHandling(data: PJsonNode): THandlingRecord =
   hand.getField("reverse", result.reverse)
   hand.getField("strafe", result.strafe)
   hand.getField("rotation", result.rotation)
-proc importAnim(data: PJsonNode, errors: var seq[string]): PAnimationRecord =
+proc importAnim(data: JsonNode, errors: var seq[string]): PAnimationRecord =
   new(result)
   result.angle = 0.0
   result.delay = 1000.0
@@ -502,26 +502,26 @@ proc importAnim(data: PJsonNode, errors: var seq[string]): PAnimationRecord =
 
   result.angle = radians(result.angle) ## comes in as degrees
   result.delay /= 1000 ## delay comes in as milliseconds
-proc importSoul(data: PJsonNode): TSoulRecord =
+proc importSoul(data: JsonNode): TSoulRecord =
   result.energy = 10000
   result.health = 1
   checkKey(data, "soul")
   let soul = data["soul"]
   soul.getField("energy", result.energy)
   soul.getField("health", result.health)
-proc importExplosion(data: PJsonNode; errors: var seq[string]): TExplosionRecord =
+proc importExplosion(data: JsonNode; errors: var seq[string]): TExplosionRecord =
   checkKey(data, "explode")
   let expl = data["explode"]
   result.anim = importAnim(expl, errors)
   result.sound = importSound(expl, errors, "sound")
-proc importSound*(data: PJsonNode; errors: var seq[string]; fieldName: string = nil): PSoundRecord =
+proc importSound*(data: JsonNode; errors: var seq[string]; fieldName: string = nil): PSoundRecord =
   if data.kind == JObject:
     checkKey(data, fieldName)
     result = newSound(data[fieldName].str, errors)
   elif data.kind == JString:
     result = newSound(data.str, errors)
 
-proc importVeh(data: PJsonNode; errors: var seq[string]): PVehicleRecord =
+proc importVeh(data: JsonNode; errors: var seq[string]): PVehicleRecord =
   new(result)
   result.playable = false
   if data.kind != JArray or data.len != 2 or
@@ -538,7 +538,7 @@ proc importVeh(data: PJsonNode; errors: var seq[string]): PVehicleRecord =
   vehdata.getField("playable", result.playable)
   if result.anim.spriteSheet.isNil and result.playable:
     result.playable = false
-proc importObject(data: PJsonNode; errors: var seq[string]): PObjectRecord =
+proc importObject(data: JsonNode; errors: var seq[string]): PObjectRecord =
   new(result)
   if data.kind != JArray or data.len != 2:
     result.name = "(broken)"
@@ -546,7 +546,7 @@ proc importObject(data: PJsonNode; errors: var seq[string]): PObjectRecord =
   result.name = data[0].str
   result.anim = importAnim(data[1], errors)
   result.physics = importPhys(data[1])
-proc importItem(data: PJsonNode; errors: var seq[string]): PItemRecord =
+proc importItem(data: JsonNode; errors: var seq[string]): PItemRecord =
   new(result)
   if data.kind != JArray or data.len != 3:
     result.name = "(broken)"
@@ -562,7 +562,7 @@ proc importItem(data: PJsonNode; errors: var seq[string]): PItemRecord =
 
   result.useSound = importSound(data[2], errors, "useSound")
 
-  case data[1].str.toLower
+  case data[1].str.toLowerAscii
   of "projectile":
     result.kind = Projectile
     if data[2]["bullet"].kind == JString:
@@ -576,15 +576,15 @@ proc importItem(data: PJsonNode; errors: var seq[string]): PItemRecord =
   of "ammo":
     result.kind = Ammo
   of "utility":
-    nil
+    discard
   else:
     errors.add "Invalid item type \""&data[1].str&"\" for item "&result.name
 
-proc importBullet(data: PJsonNode; errors: var seq[string]): PBulletRecord =
+proc importBullet(data: JsonNode; errors: var seq[string]): PBulletRecord =
   new(result)
   result.id = -1
 
-  var bdata: PJsonNode
+  var bdata: JsonNode
   if data.kind == JArray:
     result.name = data[0].str
     bdata = data[1]
diff --git a/tests/manyloc/keineschweine/lib/sg_gui.nim b/tests/manyloc/keineschweine/lib/sg_gui.nim
index ffc4e8215..b7448d9df 100644
--- a/tests/manyloc/keineschweine/lib/sg_gui.nim
+++ b/tests/manyloc/keineschweine/lib/sg_gui.nim
@@ -2,7 +2,7 @@ import
   sfml, sfml_colors,
   input_helpers, sg_packets
 from strutils import countlines
-{.deadCodeElim: on.}
+
 type
   PGuiContainer* = ref TGuiContainer
   TGuiContainer* = object of TObject
diff --git a/tests/manyloc/keineschweine/lib/sg_packets.nim b/tests/manyloc/keineschweine/lib/sg_packets.nim
index d84bf72fc..f3a0e8925 100644
--- a/tests/manyloc/keineschweine/lib/sg_packets.nim
+++ b/tests/manyloc/keineschweine/lib/sg_packets.nim
@@ -4,14 +4,14 @@ defPacketImports()
 type
   PacketID* = char
 
-template idpacket(pktName, id, s2c, c2s: expr): stmt {.immediate, dirty.} =
+template idpacket(pktName, id, s2c, c2s: untyped) {.dirty.} =
   let `H pktName`* {.inject.} = id
   defPacket(`Sc pktName`, s2c)
   defPacket(`Cs pktName`, c2s)
 
 forwardPacketT(uint8, int8)
 forwardPacketT(uint16, int16)
-forwardPacketT(TPort, int16)
+forwardPacketT(Port, int16)
 
 idPacket(Login, 'a',
   tuple[id: int32; alias: string; sessionKey: string],
@@ -22,7 +22,7 @@ defPacket(CsZoneJoinReq, tuple[session: ScLogin])
 
 defPacket(ScZoneRecord, tuple[
   name: string = "", desc: string = "",
-  ip: string = "", port: TPort = 0.Tport])
+  ip: string = "", port: Port = 0.Port])
 idPacket(ZoneList, 'z',
   tuple[network: string = "", zones: seq[ScZoneRecord]],
   tuple[time: string])
diff --git a/tests/manyloc/keineschweine/server/nim.cfg b/tests/manyloc/keineschweine/server/nim.cfg
index fdc45a8e1..211ad3003 100644
--- a/tests/manyloc/keineschweine/server/nim.cfg
+++ b/tests/manyloc/keineschweine/server/nim.cfg
@@ -1,5 +1,4 @@
 debugger = off
-deadCodeElim = on
 path = ".."
 path = "../genpacket"
 path = "../helpers"
diff --git a/tests/manyloc/nake/nake.nim b/tests/manyloc/nake/nake.nim
index 1e88fa73b..728619e47 100644
--- a/tests/manyloc/nake/nake.nim
+++ b/tests/manyloc/nake/nake.nim
@@ -67,7 +67,7 @@ else:
     for kind, key, val in getOpt():
       case kind
       of cmdLongOption, cmdShortOption:
-        case key.tolower
+        case key.tolowerAscii
         of "tasks", "t":
           printTaskList = true
         else:
diff --git a/tests/manyloc/nake/nakefile.nim b/tests/manyloc/nake/nakefile.nim
index 2055d7834..3e8609169 100644
--- a/tests/manyloc/nake/nakefile.nim
+++ b/tests/manyloc/nake/nakefile.nim
@@ -10,7 +10,7 @@ const
   ExeName = "keineschweine"
   ServerDefines = "-d:NoSFML -d:NoChipmunk"
   TestBuildDefines = "-d:escapeMenuTest -d:debugWeps -d:showFPS -d:moreNim -d:debugKeys -d:foo -d:recordMode --forceBuild"
-  ReleaseDefines = "-d:release --deadCodeElim:on"
+  ReleaseDefines = "-d:release"
   ReleaseTestDefines = "-d:debugWeps -d:debugKeys --forceBuild"
 
 task "testprofile", "..":
@@ -88,7 +88,7 @@ task "download", "download game assets":
   if existsFile(path):
     echo "The file already exists\n",
       "[R]emove  [M]ove  [Q]uit  [S]kip    Source: ", GameAssets
-    case stdin.readLine.toLower
+    case stdin.readLine.toLowerAscii
     of "r":
       removeFile path
     of "m":
@@ -120,7 +120,7 @@ task "download", "download game assets":
 
   echo "Download binary libs? Only libs for linux are available currently, enjoy the irony.\n",
     "[Y]es [N]o   Source: ", BinLibs
-  case stdin.readline.toLower
+  case stdin.readline.toLowerAscii
   of "y", "yes":
     discard ## o_O
   else:
diff --git a/tests/manyloc/standalone/barebone.nim.cfg b/tests/manyloc/standalone/barebone.nim.cfg
index bb350ff55..b956bef8e 100644
--- a/tests/manyloc/standalone/barebone.nim.cfg
+++ b/tests/manyloc/standalone/barebone.nim.cfg
@@ -1,3 +1,2 @@
 --os:standalone
---deadCodeElim:on
 --gc:none
diff --git a/tests/metatype/tbindtypedesc.nim b/tests/metatype/tbindtypedesc.nim
index b287aad01..039acfbe9 100644
--- a/tests/metatype/tbindtypedesc.nim
+++ b/tests/metatype/tbindtypedesc.nim
@@ -46,7 +46,7 @@ type
   type1 = typedesc
   type2 = typedesc
 
-proc typePairs(A, B: type1; C, D: type2) = nil
+proc typePairs(A, B: type1; C, D: type2) = discard
 
 accept typePairs(int, int, TFoo, TFOO)
 accept typePairs(TBAR, TBar, TBAR, TBAR)
@@ -55,7 +55,7 @@ accept typePairs(int, int, string, string)
 reject typePairs(TBAR, TBar, TBar, TFoo)
 reject typePairs(string, int, TBAR, TBAR)
 
-proc typePairs2[T: typedesc, U: typedesc](A, B: T; C, D: U) = nil
+proc typePairs2[T: typedesc, U: typedesc](A, B: T; C, D: U) = discard
 
 accept typePairs2(int, int, TFoo, TFOO)
 accept typePairs2(TBAR, TBar, TBAR, TBAR)
@@ -71,12 +71,12 @@ proc dontBind(a: typedesc, b: typedesc) =
 accept dontBind(int, float)
 accept dontBind(TFoo, TFoo)
 
-proc dontBind2(a, b: typedesc) = nil
+proc dontBind2(a, b: typedesc) = discard
 
 accept dontBind2(int, float)
 accept dontBind2(TBar, int)
 
-proc bindArg(T: typedesc, U: typedesc, a, b: T, c, d: U) = nil
+proc bindArg(T: typedesc, U: typedesc, a, b: T, c, d: U) = discard
 
 accept bindArg(int, string, 10, 20, "test", "nest")
 accept bindArg(int, int, 10, 20, 30, 40)
diff --git a/tests/method/tsimmeth.nim b/tests/method/tsimmeth.nim
index 11ff2674f..a057c35b7 100644
--- a/tests/method/tsimmeth.nim
+++ b/tests/method/tsimmeth.nim
@@ -6,7 +6,7 @@ discard """
 
 import strutils
 
-var x = "hello world!".toLower.toUpper
+var x = "hello world!".toLowerAscii.toUpperAscii
 x.echo()
 #OUT HELLO WORLD!
 
diff --git a/tests/misc/tmemoization.nim b/tests/misc/tmemoization.nim
index 180acd89b..840eb3b0d 100644
--- a/tests/misc/tmemoization.nim
+++ b/tests/misc/tmemoization.nim
@@ -8,7 +8,7 @@ import strutils
 proc foo(s: static[string]): string =
   static: echo s
 
-  const R = s.toUpper
+  const R = s.toUpperAscii
   return R
 
 echo foo("test 1")
diff --git a/tests/misc/tsemfold.nim b/tests/misc/tsemfold.nim
new file mode 100644
index 000000000..18c282d9e
--- /dev/null
+++ b/tests/misc/tsemfold.nim
@@ -0,0 +1,23 @@
+discard """
+  action: run
+"""
+
+doAssertRaises(OverflowError): discard low(int8) - 1'i8
+doAssertRaises(OverflowError): discard high(int8) + 1'i8
+doAssertRaises(OverflowError): discard abs(low(int8))
+doAssertRaises(DivByZeroError): discard 1 mod 0
+doAssertRaises(DivByZeroError): discard 1 div 0
+doAssertRaises(OverflowError): discard low(int8) div -1'i8
+
+doAssertRaises(OverflowError): discard low(int64) - 1'i64
+doAssertRaises(OverflowError): discard high(int64) + 1'i64
+
+type E = enum eA, eB
+doAssertRaises(OverflowError): discard eA.pred
+doAssertRaises(OverflowError): discard eB.succ
+
+doAssertRaises(OverflowError): discard low(int8) * -1
+doAssertRaises(OverflowError): discard low(int64) * -1
+doAssertRaises(OverflowError): discard high(int8) * 2
+doAssertRaises(OverflowError): discard high(int64) * 2
+
diff --git a/tests/misc/tsimplesort.nim b/tests/misc/tsimplesort.nim
index 3115863d5..e4a8e0b37 100644
--- a/tests/misc/tsimplesort.nim
+++ b/tests/misc/tsimplesort.nim
@@ -40,11 +40,11 @@ proc mustRehash(length, counter: int): bool {.inline.} =
   assert(length > counter)
   result = (length * 2 < counter * 3) or (length - counter < 4)
 
-proc nextTry(h, maxHash: THash): THash {.inline.} =
+proc nextTry(h, maxHash: Hash): Hash {.inline.} =
   result = ((5 * h) + 1) and maxHash
 
 template rawGetImpl() =
-  var h: THash = hash(key) and high(t.data) # start with real hash value
+  var h: Hash = hash(key) and high(t.data) # start with real hash value
   while t.data[h].slot != seEmpty:
     if t.data[h].key == key and t.data[h].slot == seFilled:
       return h
@@ -52,7 +52,7 @@ template rawGetImpl() =
   result = -1
 
 template rawInsertImpl() =
-  var h: THash = hash(key) and high(data)
+  var h: Hash = hash(key) and high(data)
   while data[h].slot == seFilled:
     h = nextTry(h, high(data))
   data[h].key = key
@@ -162,7 +162,7 @@ iterator values*[A](t: TCountTable[A]): int =
     if t.data[h].val != 0: yield t.data[h].val
 
 proc RawGet[A](t: TCountTable[A], key: A): int =
-  var h: THash = hash(key) and high(t.data) # start with real hash value
+  var h: Hash = hash(key) and high(t.data) # start with real hash value
   while t.data[h].val != 0:
     if t.data[h].key == key: return h
     h = nextTry(h, high(t.data))
@@ -181,7 +181,7 @@ proc hasKey*[A](t: TCountTable[A], key: A): bool =
 
 proc rawInsert[A](t: TCountTable[A], data: var seq[tuple[key: A, val: int]],
                   key: A, val: int) =
-  var h: THash = hash(key) and high(data)
+  var h: Hash = hash(key) and high(data)
   while data[h].val != 0: h = nextTry(h, high(data))
   data[h].key = key
   data[h].val = val
diff --git a/tests/misc/tunsignedmisc.nim b/tests/misc/tunsignedmisc.nim
index 4b8157ddd..fc02eee19 100644
--- a/tests/misc/tunsignedmisc.nim
+++ b/tests/misc/tunsignedmisc.nim
@@ -1,5 +1,5 @@
 discard """
-  errormsg: "number 0x123'u8 out of valid range"
+  errormsg: "number out of range: '0x123'u8'"
 """
 
 # Bug #1179
diff --git a/tests/modules/definitions.nim b/tests/modules/definitions.nim
new file mode 100644
index 000000000..edc6eaa6d
--- /dev/null
+++ b/tests/modules/definitions.nim
@@ -0,0 +1,4 @@
+var v*: int
+proc p* = echo "proc p called"
+template t* = echo "template t expanded"
+
diff --git a/tests/modules/proxy_module.nim b/tests/modules/proxy_module.nim
new file mode 100644
index 000000000..c244688cd
--- /dev/null
+++ b/tests/modules/proxy_module.nim
@@ -0,0 +1,3 @@
+import definitions
+export definitions except p
+
diff --git a/tests/notnil/tmust_compile.nim b/tests/notnil/tmust_compile.nim
index 10da154f0..a32c6c7ec 100644
--- a/tests/notnil/tmust_compile.nim
+++ b/tests/notnil/tmust_compile.nim
@@ -3,6 +3,7 @@ discard """
 """
 
 # bug #6682
+{.experimental: "notnil".}
 
 type
     Fields = enum
diff --git a/tests/notnil/tnotnil.nim b/tests/notnil/tnotnil.nim
index f65634ed6..e392b155c 100644
--- a/tests/notnil/tnotnil.nim
+++ b/tests/notnil/tnotnil.nim
@@ -2,7 +2,7 @@ discard """
   line: 22
   errormsg: "type mismatch"
 """
-
+{.experimental: "notnil".}
 type
   PObj = ref TObj not nil
   TObj = object
@@ -15,8 +15,8 @@ type
 proc p(x: string not nil): int =
   result = 45
 
-proc q(x: MyString) = nil
-proc q2(x: string) = nil
+proc q(x: MyString) = discard
+proc q2(x: string) = discard
 
 q2(nil)
 q(nil)
diff --git a/tests/notnil/tnotnil1.nim b/tests/notnil/tnotnil1.nim
index 73472752c..7f9d02295 100644
--- a/tests/notnil/tnotnil1.nim
+++ b/tests/notnil/tnotnil1.nim
@@ -4,7 +4,7 @@ discard """
 """
 
 import strutils
-
+{.experimental: "notnil".}
 
 type
   TObj = object
@@ -18,13 +18,13 @@ proc q(s: superstring) =
   echo s
 
 proc p2() =
-  var  a: string = "I am not nil"
+  var a: string = "I am not nil"
   q(a) # but this should and does not
 
 p2()
 
 proc q(x: pointer not nil) =
-  nil
+  discard
 
 proc p() =
   var x: pointer
diff --git a/tests/notnil/tnotnil2.nim b/tests/notnil/tnotnil2.nim
index bd6b8b675..6cd08de73 100644
--- a/tests/notnil/tnotnil2.nim
+++ b/tests/notnil/tnotnil2.nim
@@ -4,14 +4,14 @@ discard """
 """
 
 import strutils
-
+{.experimental: "notnil".}
 
 type
   TObj = object
     x, y: int
 
 proc q(x: pointer not nil) =
-  nil
+  discard
 
 proc p() =
   var x: pointer
diff --git a/tests/notnil/tnotnil3.nim b/tests/notnil/tnotnil3.nim
index b7c7a811d..31a4efef7 100644
--- a/tests/notnil/tnotnil3.nim
+++ b/tests/notnil/tnotnil3.nim
@@ -5,7 +5,7 @@ discard """
 
 # bug #584
 # Testprogram for 'not nil' check
-
+{.experimental: "notnil".}
 const testWithResult = true
 
 type
diff --git a/tests/notnil/tnotnil4.nim b/tests/notnil/tnotnil4.nim
index 2fa888357..4fd169827 100644
--- a/tests/notnil/tnotnil4.nim
+++ b/tests/notnil/tnotnil4.nim
@@ -2,6 +2,8 @@ discard ""
 type
    TObj = ref object
 
+{.experimental: "notnil".}
+
 proc check(a: TObj not nil) =
   echo repr(a)
 
diff --git a/tests/notnil/tnotnil_in_generic.nim b/tests/notnil/tnotnil_in_generic.nim
index 357ab2c7c..89d20f182 100644
--- a/tests/notnil/tnotnil_in_generic.nim
+++ b/tests/notnil/tnotnil_in_generic.nim
@@ -3,6 +3,7 @@ discard """
 """
 
 # bug #2216
+{.experimental: "notnil".}
 
 type
     A[T] = ref object
diff --git a/tests/notnil/tnotnil_in_objconstr.nim b/tests/notnil/tnotnil_in_objconstr.nim
index 7dce98c29..d33709906 100644
--- a/tests/notnil/tnotnil_in_objconstr.nim
+++ b/tests/notnil/tnotnil_in_objconstr.nim
@@ -2,7 +2,7 @@ discard """
   errormsg: "fields not initialized: bar"
   line: "13"
 """
-
+{.experimental: "notnil".}
 # bug #2355
 type
   Foo = object
diff --git a/tests/objects/tinherentedvalues.nim b/tests/objects/tinherentedvalues.nim
index c96a0fd6d..ad7b5f326 100644
--- a/tests/objects/tinherentedvalues.nim
+++ b/tests/objects/tinherentedvalues.nim
@@ -1,9 +1,7 @@
 discard """
-  output: '''tbObj of TC false
-false
+  output: '''tbObj of TC true
 true
-5
-false'''
+5'''
 """
 
 # bug #1053
@@ -20,10 +18,10 @@ type
 proc test(p: TA) =
   #echo "p of TB ", p of TB
   if p of TB:
-    var tbObj = TB(p)
+    #var tbObj = TB(p)
 
     # tbObj is actually no longer compatible with TC:
-    echo "tbObj of TC ", tbObj of TC
+    echo "tbObj of TC ", p of TC
 
 var v = TC()
 v.a = 1
@@ -48,8 +46,8 @@ proc isMyObject(obj: TObject) =
 
 asd.x = 5
 
-var asdCopy = TObject(asd)
-echo asdCopy of MyObject
+#var asdCopy = TObject(asd)
+#echo asdCopy of MyObject
 
 isMyObject(asd)
-isMyObject(asdCopy)
+#isMyObject(asdCopy)
diff --git a/tests/objects/tobj_asgn_dont_slice.nim b/tests/objects/tobj_asgn_dont_slice.nim
new file mode 100644
index 000000000..866b5aecc
--- /dev/null
+++ b/tests/objects/tobj_asgn_dont_slice.nim
@@ -0,0 +1,24 @@
+discard """
+  outputsub: '''ObjectAssignmentError'''
+  exitcode: "1"
+"""
+
+# bug #7637
+type
+  Fruit = object of RootObj
+    name*: string
+  Apple = object of Fruit
+  Pear = object of Fruit
+
+method eat(f: Fruit) {.base.} =
+  raise newException(Exception, "PURE VIRTUAL CALL")
+
+method eat(f: Apple) =
+  echo "fruity"
+
+method eat(f: Pear) =
+  echo "juicy"
+
+let basket = [Apple(name:"a"), Pear(name:"b")]
+
+eat(basket[0])
diff --git a/tests/objects/tobjconstr.nim b/tests/objects/tobjconstr.nim
index d1f3c8bdb..7238d10c7 100644
--- a/tests/objects/tobjconstr.nim
+++ b/tests/objects/tobjconstr.nim
@@ -9,8 +9,8 @@ discard """
 (k: kindA, a: (x: "abc", z: [1, 8, 3]), method: ())
 (k: kindA, a: (x: "abc", z: [1, 9, 3]), method: ())
 (k: kindA, a: (x: "abc", z: [1, 10, 3]), method: ())
-(x: 123)
-(x: 123)
+(y: 0, x: 123)
+(y: 678, x: 123)
 (z: 89, y: 0, x: 128)
 (y: 678, x: 123)
 (y: 678, x: 123)
@@ -33,7 +33,6 @@ type
       `method`: TEmpty # bug #1791
 
 proc `$`[T](s: seq[T]): string =
-  # XXX why is that not in the stdlib?
   result = "["
   for i, x in s:
     if i > 0: result.add(", ")
@@ -59,7 +58,7 @@ type
 # inherited fields are ignored, so no fields are set
 when true:
   var
-    o: A
+    o: B
   o = B(x: 123)
   echo o
   o = B(y: 678, x: 123)
diff --git a/tests/objvariant/tcheckedfield1.nim b/tests/objvariant/tcheckedfield1.nim
index 56d784a2b..a7f232c5b 100644
--- a/tests/objvariant/tcheckedfield1.nim
+++ b/tests/objvariant/tcheckedfield1.nim
@@ -6,7 +6,7 @@ discard """
 import strutils
 
 {.warning[ProveField]: on.}
-
+{.experimental: "notnil".}
 type
   TNodeKind = enum
     nkBinary, nkTernary, nkStr
diff --git a/tests/objvariant/temptycaseobj.nim b/tests/objvariant/temptycaseobj.nim
index 5c012746e..53171e054 100644
--- a/tests/objvariant/temptycaseobj.nim
+++ b/tests/objvariant/temptycaseobj.nim
@@ -1,6 +1,6 @@
 discard """
   line: 11
-  errormsg: "identifier expected, but found 'keyword of'"
+  errormsg: "identifier expected, but got 'keyword of'"
 """
 
 type
diff --git a/tests/osproc/tclose.nim b/tests/osproc/tclose.nim
new file mode 100644
index 000000000..d466b466a
--- /dev/null
+++ b/tests/osproc/tclose.nim
@@ -0,0 +1,24 @@
+discard """
+  exitcode: 0
+"""
+
+when defined(linux):
+  import osproc, os
+
+  proc countFds(): int =
+    result = 0
+    for i in walkDir("/proc/self/fd"):
+      result += 1
+
+  let initCount = countFds()
+
+  let p = osproc.startProcess("echo", options={poUsePath})
+  assert countFds() == initCount + 3
+  p.close
+  assert countFds() == initCount
+
+  let p1 = osproc.startProcess("echo", options={poUsePath})
+  discard p1.inputStream
+  assert countFds() == initCount + 3
+  p.close
+  assert countFds() == initCount
diff --git a/tests/overload/tissue966.nim b/tests/overload/tissue966.nim
index c5b28e217..d0a723875 100644
--- a/tests/overload/tissue966.nim
+++ b/tests/overload/tissue966.nim
@@ -5,7 +5,7 @@ discard """
 type
   PTest = ref object
 
-proc test(x: PTest, y: int) = nil
+proc test(x: PTest, y: int) = discard
 
 var buf: PTest
 buf.test()
diff --git a/tests/overload/toverprc.nim b/tests/overload/toverprc.nim
index 112eae096..9be2203f6 100644
--- a/tests/overload/toverprc.nim
+++ b/tests/overload/toverprc.nim
@@ -11,7 +11,7 @@ proc parseInt(x: float): int {.noSideEffect.} = discard
 proc parseInt(x: bool): int {.noSideEffect.} = discard
 proc parseInt(x: float32): int {.noSideEffect.} = discard
 proc parseInt(x: int8): int {.noSideEffect.} = discard
-proc parseInt(x: TFile): int {.noSideEffect.} = discard
+proc parseInt(x: File): int {.noSideEffect.} = discard
 proc parseInt(x: char): int {.noSideEffect.} = discard
 proc parseInt(x: int16): int {.noSideEffect.} = discard
 
diff --git a/tests/overload/tparam_forwarding.nim b/tests/overload/tparam_forwarding.nim
index c1b276bfc..b0eea42c7 100644
--- a/tests/overload/tparam_forwarding.nim
+++ b/tests/overload/tparam_forwarding.nim
@@ -6,6 +6,10 @@ output: '''baz
 a
 b
 c
+x: 1, y: test 1
+x: 2, y: test 2
+x: 10, y: test 3
+x: 4, y: test 4
 '''
 """
 
@@ -35,3 +39,15 @@ templateForwarding fooVarargs, "test".len > 3, Foo(x: 10), Foo(x: 100), Foo(x: 1
 
 procForwarding "a", "b", "c"
 
+proc hasKeywordArgs(x = 10, y = "y") =
+  echo "x: ", x, ", y: ", y
+
+proc hasRegularArgs(x: int, y: string) =
+  echo "x: ", x, ", y: ", y
+
+templateForwarding(hasRegularArgs, true, 1, "test 1")
+templateForwarding hasKeywordArgs, true, 2, "test 2"
+
+templateForwarding(hasKeywordArgs, true, y = "test 3")
+templateForwarding hasKeywordArgs, true, y = "test 4", x = 4
+
diff --git a/tests/parallel/tparfind.nim b/tests/parallel/tparfind.nim
index 9de5012f5..4b3610c67 100644
--- a/tests/parallel/tparfind.nim
+++ b/tests/parallel/tparfind.nim
@@ -4,7 +4,7 @@ discard """
 
 import threadpool, sequtils
 
-{.experimental.}
+{.experimental: "parallel".}
 
 proc linearFind(a: openArray[int]; x, offset: int): int =
   for i, y in a:
diff --git a/tests/parallel/twaitany.nim b/tests/parallel/twaitany.nim
new file mode 100644
index 000000000..69136a3b6
--- /dev/null
+++ b/tests/parallel/twaitany.nim
@@ -0,0 +1,35 @@
+discard """
+  output: '''true'''
+"""
+
+# bug #7638
+import threadpool, os, strformat
+
+proc timer(d: int): int =
+  #echo fmt"sleeping {d}"
+  sleep(d)
+  #echo fmt"done {d}"
+  return d
+
+var durations = [1000, 2000, 3000, 4000, 5000]
+var tasks: seq[FlowVarBase] = @[]
+var results: seq[int] = @[]
+
+for i in 0 .. durations.high:
+  tasks.add spawn timer(durations[i])
+
+var index = awaitAny(tasks)
+while index != -1:
+  results.add ^cast[FlowVar[int]](tasks[index])
+  tasks.del(index)
+  #echo repr results
+  index = awaitAny(tasks)
+
+doAssert results.len == 5
+doAssert 1000 in results
+doAssert 2000 in results
+doAssert 3000 in results
+doAssert 4000 in results
+doAssert 5000 in results
+sync()
+echo "true"
diff --git a/tests/parser/tbraces.nim b/tests/parser/tbraces.nim
deleted file mode 100644
index 86c854546..000000000
--- a/tests/parser/tbraces.nim
+++ /dev/null
@@ -1,432 +0,0 @@
-#? braces
-
-#
-#
-#            Nim's Runtime Library
-#        (c) Copyright 2015 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## This module implements some common generic algorithms.
-
-type
-  SortOrder* = enum {  ## sort order
-    Descending, Ascending
-  }
-
-
-type(
-  DummyAlias = int
-  OtherAlias = distinct char
-
-  SomeObject = object of RootObj { ## declaration here
-    fieldA, fieldB: int
-    case order: SortOrder {
-      of Descending {a: string}
-      of Ascending {b: seq[char]}
-    }
-  }
-
-  MyConcept = concept x {
-     x is int
-  }
-)
-
-{.deprecated: [TSortOrder: SortOrder].}
-
-
-proc `*`*(x: int, order: SortOrder): int @inline {
-  ## flips `x` if ``order == Descending``;
-  ## if ``order == Ascending`` then `x` is returned.
-  ## `x` is supposed to be the result of a comparator, ie ``< 0`` for
-  ## *less than*, ``== 0`` for *equal*, ``> 0`` for *greater than*.
-  var y = order.ord - 1
-  result = (x xor y) - y
-}
-
-proc fill*[T](a: var openArray[T], first, last: Natural, value: T) {
-  ## fills the array ``a[first..last]`` with `value`.
-  var x = first
-  while x <= last {
-    a[x] = value
-    inc(x)
-  }
-}
-
-proc fill*[T](a: var openArray[T], value: T) {
-  ## fills the array `a` with `value`.
-  fill(a, 0, a.high, value)
-}
-
-proc reverse*[T](a: var openArray[T], first, last: Natural) {
-  ## reverses the array ``a[first..last]``.
-  var x = first
-  var y = last
-  while x < y {
-    swap(a[x], a[y])
-    dec(y)
-    inc(x)
-  }
-}
-
-proc reverse*[T](a: var openArray[T]) {
-  ## reverses the array `a`.
-  reverse(a, 0, a.high)
-}
-
-proc reversed*[T](a: openArray[T], first: Natural, last: int): seq[T] {
-  ## returns the reverse of the array `a[first..last]`.
-  assert last >= first-1
-  var i = last - first
-  var x = first.int
-  result = newSeq[T](i + 1)
-  while i >= 0 {
-    result[i] = a[x]
-    dec(i)
-    inc(x)
-  }
-}
-
-proc reversed*[T](a: openArray[T]): seq[T] {
-  ## returns the reverse of the array `a`.
-  reversed(a, 0, a.high)
-}
-
-proc binarySearch*[T](a: openArray[T], key: T): int {
-  ## binary search for `key` in `a`. Returns -1 if not found.
-  var b = len(a)
-  while result < b {
-    var mid = (result + b) div 2
-    if a[mid] < key { result = mid + 1 } else { b = mid }
-  }
-  if result >= len(a) or a[result] != key { result = -1 }
-}
-
-proc smartBinarySearch*[T](a: openArray[T], key: T): int {
-  ## ``a.len`` must be a power of 2 for this to work.
-  var step = a.len div 2
-  while step > 0 {
-    if a[result or step] <= key { result = result or step }
-    step = step shr 1
-  }
-  if a[result] != key { result = -1 }
-}
-
-const (
-  onlySafeCode = true
-)
-
-proc lowerBound*[T](a: openArray[T], key: T, cmp: proc(x,y: T): int @closure): int {
-  ## same as binarySearch except that if key is not in `a` then this
-  ## returns the location where `key` would be if it were. In other
-  ## words if you have a sorted sequence and you call
-  ## insert(thing, elm, lowerBound(thing, elm))
-  ## the sequence will still be sorted.
-  ##
-  ## `cmp` is the comparator function to use, the expected return values are
-  ## the same as that of system.cmp.
-  ##
-  ## example::
-  ##
-  ##   var arr = @[1,2,3,5,6,7,8,9]
-  ##   arr.insert(4, arr.lowerBound(4))
-  ##   # after running the above arr is `[1,2,3,4,5,6,7,8,9]`
-  result = a.low
-  var count = a.high - a.low + 1
-  var step, pos: int
-  while count != 0 {
-    step = count div 2
-    pos = result + step
-    if cmp(a[pos], key) < 0 {
-      result = pos + 1
-      count -= step + 1
-    } else {
-      count = step
-    }
-  }
-}
-
-proc lowerBound*[T](a: openArray[T], key: T): int { lowerBound(a, key, cmp[T]) }
-proc merge[T](a, b: var openArray[T], lo, m, hi: int,
-              cmp: proc (x, y: T): int @closure, order: SortOrder) {
-  template `<-` (a, b) {
-    when false {
-      a = b
-    } elif onlySafeCode {
-      shallowCopy(a, b)
-    } else {
-      copyMem(addr(a), addr(b), sizeof(T))
-    }
-  }
-  # optimization: If max(left) <= min(right) there is nothing to do!
-  # 1 2 3 4  ## 5 6 7 8
-  # -> O(n) for sorted arrays.
-  # On random data this safes up to 40% of merge calls
-  if cmp(a[m], a[m+1]) * order <= 0 { return }
-  var j = lo
-  # copy a[j..m] into b:
-  assert j <= m
-  when onlySafeCode {
-    var bb = 0
-    while j <= m {
-      b[bb] <- a[j]
-      inc(bb)
-      inc(j)
-    }
-  } else {
-    copyMem(addr(b[0]), addr(a[j]), sizeof(T)*(m-j+1))
-    j = m+1
-  }
-  var i = 0
-  var k = lo
-  # copy proper element back:
-  while k < j and j <= hi {
-    if cmp(b[i], a[j]) * order <= 0 {
-      a[k] <- b[i]
-      inc(i)
-    } else {
-      a[k] <- a[j]
-      inc(j)
-    }
-    inc(k)
-  }
-  # copy rest of b:
-  when onlySafeCode {
-    while k < j {
-      a[k] <- b[i]
-      inc(k)
-      inc(i)
-    }
-  } else {
-    if k < j { copyMem(addr(a[k]), addr(b[i]), sizeof(T)*(j-k)) }
-  }
-}
-
-proc sort*[T](a: var openArray[T],
-              cmp: proc (x, y: T): int @closure,
-              order = SortOrder.Ascending) {
-  ## Default Nim sort (an implementation of merge sort). The sorting
-  ## is guaranteed to be stable and the worst case is guaranteed to
-  ## be O(n log n).
-  ## The current implementation uses an iterative
-  ## mergesort to achieve this. It uses a temporary sequence of
-  ## length ``a.len div 2``. Currently Nim does not support a
-  ## sensible default argument for ``cmp``, so you have to provide one
-  ## of your own. However, the ``system.cmp`` procs can be used:
-  ##
-  ## .. code-block:: nim
-  ##
-  ##    sort(myIntArray, system.cmp[int])
-  ##
-  ##    # do not use cmp[string] here as we want to use the specialized
-  ##    # overload:
-  ##    sort(myStrArray, system.cmp)
-  ##
-  ## You can inline adhoc comparison procs with the `do notation
-  ## <manual.html#procedures-do-notation>`_. Example:
-  ##
-  ## .. code-block:: nim
-  ##
-  ##   people.sort do (x, y: Person) -> int:
-  ##     result = cmp(x.surname, y.surname)
-  ##     if result == 0:
-  ##       result = cmp(x.name, y.name)
-  var n = a.len
-  var b: seq[T]
-  newSeq(b, n div 2)
-  var s = 1
-  while s < n {
-    var m = n-1-s
-    while m >= 0 {
-      merge(a, b, max(m-s+1, 0), m, m+s, cmp, order)
-      dec(m, s*2)
-    }
-    s = s*2
-  }
-}
-
-proc sorted*[T](a: openArray[T], cmp: proc(x, y: T): int {.closure.},
-                order = SortOrder.Ascending): seq[T] {
-  ## returns `a` sorted by `cmp` in the specified `order`.
-  result = newSeq[T](a.len)
-  for i in 0 .. a.high { result[i] = a[i] }
-  sort(result, cmp, order)
-}
-
-template sortedByIt*(seq1, op: untyped): untyped {
-  ## Convenience template around the ``sorted`` proc to reduce typing.
-  ##
-  ## The template injects the ``it`` variable which you can use directly in an
-  ## expression. Example:
-  ##
-  ## .. code-block:: nim
-  ##
-  ##   type Person = tuple[name: string, age: int]
-  ##   var
-  ##     p1: Person = (name: "p1", age: 60)
-  ##     p2: Person = (name: "p2", age: 20)
-  ##     p3: Person = (name: "p3", age: 30)
-  ##     p4: Person = (name: "p4", age: 30)
-  ##     people = @[p1,p2,p4,p3]
-  ##
-  ##   echo people.sortedByIt(it.name)
-  ##
-  ## Because the underlying ``cmp()`` is defined for tuples you can do
-  ## a nested sort like in the following example:
-  ##
-  ## .. code-block:: nim
-  ##
-  ##   echo people.sortedByIt((it.age, it.name))
-  ##
-  var result = sorted(seq1, proc(x, y: type[seq1[0]]): int {
-    var it {.inject.} = x
-    let a = op
-    it = y
-    let b = op
-    result = cmp(a, b)
-  })
-  result
-}
-
-proc isSorted*[T](a: openarray[T],
-                 cmp: proc(x, y: T): int {.closure.},
-                 order = SortOrder.Ascending): bool {
-  ## Checks to see whether `a` is already sorted in `order`
-  ## using `cmp` for the comparison. Parameters identical
-  ## to `sort`
-  result = true
-  for i in 0..<len(a)-1 {
-    case cmp(a[i],a[i+1]) * order > 0 {
-      of true { return false }
-      of false {}
-    }
-  }
-}
-
-proc product*[T](x: openArray[seq[T]]): seq[seq[T]] {
-  ## produces the Cartesian product of the array. Warning: complexity
-  ## may explode.
-  result = newSeq[seq[T]]()
-  if x.len == 0 { return }
-  if x.len == 1 {
-    result = @x
-    return
-  }
-  var (
-    indexes = newSeq[int](x.len)
-    initial = newSeq[int](x.len)
-    index = 0
-  )
-  var next = newSeq[T]()
-  next.setLen(x.len)
-  for i in 0..(x.len-1) {
-    if len(x[i]) == 0 { return }
-    initial[i] = len(x[i])-1
-  }
-  indexes = initial
-  while true {
-    while indexes[index] == -1 {
-      indexes[index] = initial[index]
-      index += 1
-      if index == x.len { return }
-      indexes[index] -= 1
-    }
-    for ni, i in indexes {
-      next[ni] = x[ni][i]
-    }
-    var res: seq[T]
-    shallowCopy(res, next)
-    result.add(res)
-    index = 0
-    indexes[index] -= 1
-  }
-}
-
-proc nextPermutation*[T](x: var openarray[T]): bool {.discardable.} {
-  ## Calculates the next lexicographic permutation, directly modifying ``x``.
-  ## The result is whether a permutation happened, otherwise we have reached
-  ## the last-ordered permutation.
-  ##
-  ## .. code-block:: nim
-  ##
-  ##     var v = @[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
-  ##     v.nextPermutation()
-  ##     echo v # @[0, 1, 2, 3, 4, 5, 6, 7, 9, 8]
-  if x.len < 2 {
-    return false }
-
-  var i = x.high
-  while i > 0 and x[i-1] >= x[i] { dec i }
-  if i == 0 { return false }
-
-  var j = x.high
-  while j >= i and x[j] <= x[i-1] { dec j }
-
-  swap x[j], x[i-1]
-  x.reverse(i, x.high)
-
-  result = true
-}
-
-proc prevPermutation*[T](x: var openarray[T]): bool @discardable {
-  ## Calculates the previous lexicographic permutation, directly modifying
-  ## ``x``.  The result is whether a permutation happened, otherwise we have
-  ## reached the first-ordered permutation.
-  ##
-  ## .. code-block:: nim
-  ##
-  ##     var v = @[0, 1, 2, 3, 4, 5, 6, 7, 9, 8]
-  ##     v.prevPermutation()
-  ##     echo v # @[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
-  if x.len < 2 { return false }
-
-  var i = x.high
-  while i > 0 and x[i-1] <= x[i] {
-    dec i
-  }
-  if i == 0 { return false }
-
-  x.reverse(i, x.high)
-
-  var j = x.high
-  while j >= i and x[j-1] < x[i-1] {
-    dec j
-  }
-  swap x[i-1], x[j]
-
-  result = true
-}
-
-when isMainModule {
-  # Tests for lowerBound
-  var arr = @[1,2,3,5,6,7,8,9]
-  assert arr.lowerBound(0) == 0
-  assert arr.lowerBound(4) == 3
-  assert arr.lowerBound(5) == 3
-  assert arr.lowerBound(10) == 8
-  arr = @[1,5,10]
-  try {
-    assert arr.lowerBound(4) == 1
-    assert arr.lowerBound(5) == 1
-    assert arr.lowerBound(6) == 2
-  } except ValueError {}
-  # Tests for isSorted
-  var srt1 = [1,2,3,4,4,4,4,5]
-  var srt2 = ["iello","hello"]
-  var srt3 = [1.0,1.0,1.0]
-  var srt4: seq[int] = @[]
-  assert srt1.isSorted(cmp) == true
-  assert srt2.isSorted(cmp) == false
-  assert srt3.isSorted(cmp) == true
-  var srtseq = newSeq[int]()
-  assert srtseq.isSorted(cmp) == true
-  # Tests for reversed
-  var arr1 = @[0,1,2,3,4]
-  assert arr1.reversed() == @[4,3,2,1,0]
-  for i in 0 .. high(arr1) {
-    assert arr1.reversed(0, i) == arr1.reversed()[high(arr1) - i .. high(arr1)]
-    assert arr1.reversed(i, high(arr1)) == arr1.reversed()[0 .. high(arr1) - i]
-  }
-}
diff --git a/tests/parser/tcommand_as_expr.nim b/tests/parser/tcommand_as_expr.nim
index a244c8767..b25ec4bd8 100644
--- a/tests/parser/tcommand_as_expr.nim
+++ b/tests/parser/tcommand_as_expr.nim
@@ -2,7 +2,10 @@ discard """
   output: '''140
 5-120-120
 359
-77'''
+77
+-4
+-1
+-1'''
 """
 #import math
 import sequtils
@@ -25,3 +28,11 @@ let a = [2,4,8].map do (d:int) -> int: d + 1
 echo a[0], a[1], a[2]
 
 echo(foo 8, foo 8)
+
+# bug #7582
+proc f(x: int): int = x
+
+echo f -4
+
+echo int -1 # doesn't compile
+echo int `-` 1 # compiles
diff --git a/tests/parser/tinvcolonlocation1.nim b/tests/parser/tinvcolonlocation1.nim
index cacde48bd..2fddab2f8 100644
--- a/tests/parser/tinvcolonlocation1.nim
+++ b/tests/parser/tinvcolonlocation1.nim
@@ -1,8 +1,8 @@
 discard """
   file: "tinvcolonlocation1.nim"
   line: 8
-  column: 3
-  errormsg: "':' expected"
+  column: 7
+  errormsg: "expected: ':', but got: 'echo'"
 """
 try #<- missing ':'
   echo "try"
diff --git a/tests/parser/tinvcolonlocation2.nim b/tests/parser/tinvcolonlocation2.nim
index 2b6a92b9d..4251598b9 100644
--- a/tests/parser/tinvcolonlocation2.nim
+++ b/tests/parser/tinvcolonlocation2.nim
@@ -1,8 +1,8 @@
 discard """
   file: "tinvcolonlocation2.nim"
   line: 11
-  column: 1
-  errormsg: "':' expected"
+  column: 8
+  errormsg: "expected: ':', but got: 'keyword finally'"
 """
 try:
   echo "try"
diff --git a/tests/parser/tinvcolonlocation3.nim b/tests/parser/tinvcolonlocation3.nim
index 2b30b1dbe..a8db658eb 100644
--- a/tests/parser/tinvcolonlocation3.nim
+++ b/tests/parser/tinvcolonlocation3.nim
@@ -1,8 +1,8 @@
 discard """
   file: "tinvcolonlocation3.nim"
   line: 12
-  column: 3
-  errormsg: "':' expected"
+  column: 7
+  errormsg: "expected: ':', but got: 'echo'"
 """
 try:
   echo "try"
diff --git a/tests/parser/twhen_in_enum.nim b/tests/parser/twhen_in_enum.nim
index d4a3ea56a..3890b686e 100644
--- a/tests/parser/twhen_in_enum.nim
+++ b/tests/parser/twhen_in_enum.nim
@@ -1,5 +1,5 @@
 discard """
-  errormsg: "identifier expected, but found 'keyword when'"
+  errormsg: "identifier expected, but got 'keyword when'"
 """
 
 # bug #2123
diff --git a/tests/parser/twrongcmdsyntax.nim b/tests/parser/twrongcmdsyntax.nim
index affe72c34..c2962bed4 100644
--- a/tests/parser/twrongcmdsyntax.nim
+++ b/tests/parser/twrongcmdsyntax.nim
@@ -1,5 +1,5 @@
 discard """
-  errormsg: '''identifier expected, but found 'echo 4'''
+  errormsg: '''in expression '4 2': identifier expected, but found '4'''
   line: 6
 """
 
diff --git a/tests/pragmas/tcustom_pragma.nim b/tests/pragmas/tcustom_pragma.nim
index 28a8713ce..33a4a7e65 100644
--- a/tests/pragmas/tcustom_pragma.nim
+++ b/tests/pragmas/tcustom_pragma.nim
@@ -51,6 +51,9 @@ block: # A bit more advanced case
 
   static: assert(hasCustomPragma(myproc, alternativeKey))
 
+  const hasFieldCustomPragma = s.field.hasCustomPragma(defaultValue)
+  static: assert(hasFieldCustomPragma == false)
+
   # pragma on an object
   static:
     assert Subfield.hasCustomPragma(defaultValue)
@@ -71,6 +74,8 @@ block: # ref types
     MyFile {.defaultValue: "closed".} = ref object
       path {.defaultValue: "invalid".}: string
 
+    TypeWithoutPragma = object
+
   var s = NodeRef()
 
   const
@@ -91,7 +96,7 @@ block: # ref types
 
   var ptrS = NodePtr(nil)
   const
-    ptrRightSerKey = getCustomPragmaVal(s.right, serializationKey)
+    ptrRightSerKey = getCustomPragmaVal(ptrS.right, serializationKey)
   static:
     assert ptrRightSerKey == "r"
 
@@ -103,6 +108,9 @@ block: # ref types
     assert fileDefVal == "closed"
     assert filePathDefVal == "invalid"
 
+  static:
+    assert TypeWithoutPragma.hasCustomPragma(defaultValue) == false
+
 block:
   type
     VariantKind = enum
diff --git a/tests/pragmas/tnoreturn.nim b/tests/pragmas/tnoreturn.nim
index 4d00c6034..bb59b1c71 100644
--- a/tests/pragmas/tnoreturn.nim
+++ b/tests/pragmas/tnoreturn.nim
@@ -2,13 +2,16 @@ discard """
 ccodeCheck: "\\i @'__attribute__((noreturn))' .*"
 """
 
-proc noret1*(i: int) {.noreturn.} = 
+proc noret1*(i: int) {.noreturn.} =
   echo i
 
 
-proc noret2*(i: int): void {.noreturn.} = 
+proc noret2*(i: int): void {.noreturn.} =
   echo i
 
+noret1(1)
+noret2(2)
+
 var p {.used.}: proc(i: int): int
 doAssert(not compiles(
   p = proc(i: int): int {.noreturn.} = i # noreturn lambda returns int
diff --git a/tests/proc/tprocredef.nim b/tests/proc/tprocredef.nim
index 86ed92b62..4ec771510 100644
--- a/tests/proc/tprocredef.nim
+++ b/tests/proc/tprocredef.nim
@@ -4,6 +4,6 @@ discard """
   errormsg: "redefinition of \'foo\'"
 """
 
-proc foo(a: int, b: string) = nil
-proc foo(a: int, b: string) = nil
+proc foo(a: int, b: string) = discard
+proc foo(a: int, b: string) = discard
 
diff --git a/tests/range/tsubrange.nim b/tests/range/tsubrange.nim
index 6f88c5a22..914e7c6e7 100644
--- a/tests/range/tsubrange.nim
+++ b/tests/range/tsubrange.nim
@@ -7,7 +7,7 @@ type
   TRange = range[0..40]
 
 proc p(r: TRange) =
-  nil
+  discard
 
 var
   r: TRange
diff --git a/tests/rodfiles/deadg.nim b/tests/rodfiles/deadg.nim
index 587608e14..0aee59bb8 100644
--- a/tests/rodfiles/deadg.nim
+++ b/tests/rodfiles/deadg.nim
@@ -1,6 +1,3 @@
-
-{.deadCodeElim: on.}
-
 proc p1*(x, y: int): int =
   result = x + y
 
diff --git a/tests/stdlib/tpegs.nim b/tests/stdlib/tpegs.nim
deleted file mode 100644
index e2a5a1715..000000000
--- a/tests/stdlib/tpegs.nim
+++ /dev/null
@@ -1,1770 +0,0 @@
-discard """
-  output: '''this
-is
-an
-example
-d
-e
-f
-('keyvalue' 'key'*)'''
-"""
-# PEGs module turned out to be a good test to detect memory management bugs.
-
-include "system/inclrtl"
-
-const
-  useUnicode = true ## change this to deactivate proper UTF-8 support
-
-import
-  strutils
-
-when useUnicode:
-  import unicode
-
-const
-  InlineThreshold = 5  ## number of leaves; -1 to disable inlining
-  MaxSubpatterns* = 10 ## defines the maximum number of subpatterns that
-                       ## can be captured. More subpatterns cannot be captured!
-
-type
-  TPegKind = enum
-    pkEmpty,
-    pkAny,              ## any character (.)
-    pkAnyRune,          ## any Unicode character (_)
-    pkNewLine,          ## CR-LF, LF, CR
-    pkLetter,           ## Unicode letter
-    pkLower,            ## Unicode lower case letter
-    pkUpper,            ## Unicode upper case letter
-    pkTitle,            ## Unicode title character
-    pkWhitespace,       ## Unicode whitespace character
-    pkTerminal,
-    pkTerminalIgnoreCase,
-    pkTerminalIgnoreStyle,
-    pkChar,             ## single character to match
-    pkCharChoice,
-    pkNonTerminal,
-    pkSequence,         ## a b c ... --> Internal DSL: peg(a, b, c)
-    pkOrderedChoice,    ## a / b / ... --> Internal DSL: a / b or /[a, b, c]
-    pkGreedyRep,        ## a*     --> Internal DSL: *a
-                        ## a+     --> (a a*)
-    pkGreedyRepChar,    ## x* where x is a single character (superop)
-    pkGreedyRepSet,     ## [set]* (superop)
-    pkGreedyAny,        ## .* or _* (superop)
-    pkOption,           ## a?     --> Internal DSL: ?a
-    pkAndPredicate,     ## &a     --> Internal DSL: &a
-    pkNotPredicate,     ## !a     --> Internal DSL: !a
-    pkCapture,          ## {a}    --> Internal DSL: capture(a)
-    pkBackRef,          ## $i     --> Internal DSL: backref(i)
-    pkBackRefIgnoreCase,
-    pkBackRefIgnoreStyle,
-    pkSearch,           ## @a     --> Internal DSL: !*a
-    pkCapturedSearch,   ## {@} a  --> Internal DSL: !*\a
-    pkRule,             ## a <- b
-    pkList,             ## a, b
-    pkStartAnchor       ## ^      --> Internal DSL: startAnchor()
-  TNonTerminalFlag = enum
-    ntDeclared, ntUsed
-  TNonTerminal {.final.} = object ## represents a non terminal symbol
-    name: string                  ## the name of the symbol
-    line: int                     ## line the symbol has been declared/used in
-    col: int                      ## column the symbol has been declared/used in
-    flags: set[TNonTerminalFlag]  ## the nonterminal's flags
-    rule: TNode                   ## the rule that the symbol refers to
-  TNode {.final, shallow.} = object
-    case kind: TPegKind
-    of pkEmpty..pkWhitespace: nil
-    of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle: term: string
-    of pkChar, pkGreedyRepChar: ch: char
-    of pkCharChoice, pkGreedyRepSet: charChoice: ref set[char]
-    of pkNonTerminal: nt: PNonTerminal
-    of pkBackRef..pkBackRefIgnoreStyle: index: range[0..MaxSubpatterns]
-    else: sons: seq[TNode]
-  PNonTerminal* = ref TNonTerminal
-
-  TPeg* = TNode ## type that represents a PEG
-
-proc term*(t: string): TPeg {.rtl, extern: "npegs$1Str".} =
-  ## constructs a PEG from a terminal string
-  if t.len != 1:
-    result.kind = pkTerminal
-    result.term = t
-  else:
-    result.kind = pkChar
-    result.ch = t[0]
-
-proc termIgnoreCase*(t: string): TPeg {.
-  rtl, extern: "npegs$1".} =
-  ## constructs a PEG from a terminal string; ignore case for matching
-  result.kind = pkTerminalIgnoreCase
-  result.term = t
-
-proc termIgnoreStyle*(t: string): TPeg {.
-  rtl, extern: "npegs$1".} =
-  ## constructs a PEG from a terminal string; ignore style for matching
-  result.kind = pkTerminalIgnoreStyle
-  result.term = t
-
-proc term*(t: char): TPeg {.rtl, extern: "npegs$1Char".} =
-  ## constructs a PEG from a terminal char
-  assert t != '\0'
-  result.kind = pkChar
-  result.ch = t
-
-proc charSet*(s: set[char]): TPeg {.rtl, extern: "npegs$1".} =
-  ## constructs a PEG from a character set `s`
-  assert '\0' notin s
-  result.kind = pkCharChoice
-  new(result.charChoice)
-  result.charChoice[] = s
-
-proc len(a: TPeg): int {.inline.} = return a.sons.len
-proc add(d: var TPeg, s: TPeg) {.inline.} = add(d.sons, s)
-
-proc copyPeg(a: TPeg): TPeg =
-  result.kind = a.kind
-  case a.kind
-  of pkEmpty..pkWhitespace: discard
-  of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle:
-    result.term = a.term
-  of pkChar, pkGreedyRepChar:
-    result.ch = a.ch
-  of pkCharChoice, pkGreedyRepSet:
-    new(result.charChoice)
-    result.charChoice[] = a.charChoice[]
-  of pkNonTerminal: result.nt = a.nt
-  of pkBackRef..pkBackRefIgnoreStyle:
-    result.index = a.index
-  else:
-    result.sons = a.sons
-
-proc addChoice(dest: var TPeg, elem: TPeg) =
-  var L = dest.len-1
-  if L >= 0 and dest.sons[L].kind == pkCharChoice:
-    # caution! Do not introduce false aliasing here!
-    case elem.kind
-    of pkCharChoice:
-      dest.sons[L] = charSet(dest.sons[L].charChoice[] + elem.charChoice[])
-    of pkChar:
-      dest.sons[L] = charSet(dest.sons[L].charChoice[] + {elem.ch})
-    else: add(dest, elem)
-  else: add(dest, elem)
-
-template multipleOp(k: TPegKind, localOpt) =
-  result.kind = k
-  result.sons = @[]
-  for x in items(a):
-    if x.kind == k:
-      for y in items(x.sons):
-        localOpt(result, y)
-    else:
-      localOpt(result, x)
-  if result.len == 1:
-    result = result.sons[0]
-
-proc `/`*(a: varargs[TPeg]): TPeg {.
-  rtl, extern: "npegsOrderedChoice".} =
-  ## constructs an ordered choice with the PEGs in `a`
-  multipleOp(pkOrderedChoice, addChoice)
-
-proc addSequence(dest: var TPeg, elem: TPeg) =
-  var L = dest.len-1
-  if L >= 0 and dest.sons[L].kind == pkTerminal:
-    # caution! Do not introduce false aliasing here!
-    case elem.kind
-    of pkTerminal:
-      dest.sons[L] = term(dest.sons[L].term & elem.term)
-    of pkChar:
-      dest.sons[L] = term(dest.sons[L].term & elem.ch)
-    else: add(dest, elem)
-  else: add(dest, elem)
-
-proc sequence*(a: varargs[TPeg]): TPeg {.
-  rtl, extern: "npegs$1".} =
-  ## constructs a sequence with all the PEGs from `a`
-  multipleOp(pkSequence, addSequence)
-
-proc `?`*(a: TPeg): TPeg {.rtl, extern: "npegsOptional".} =
-  ## constructs an optional for the PEG `a`
-  if a.kind in {pkOption, pkGreedyRep, pkGreedyAny, pkGreedyRepChar,
-                pkGreedyRepSet}:
-    # a* ?  --> a*
-    # a? ?  --> a?
-    result = a
-  else:
-    result.kind = pkOption
-    result.sons = @[a]
-
-proc `*`*(a: TPeg): TPeg {.rtl, extern: "npegsGreedyRep".} =
-  ## constructs a "greedy repetition" for the PEG `a`
-  case a.kind
-  of pkGreedyRep, pkGreedyRepChar, pkGreedyRepSet, pkGreedyAny, pkOption:
-    assert false
-    # produces endless loop!
-  of pkChar:
-    result.kind = pkGreedyRepChar
-    result.ch = a.ch
-  of pkCharChoice:
-    result.kind = pkGreedyRepSet
-    result.charChoice = a.charChoice # copying a reference suffices!
-  of pkAny, pkAnyRune:
-    result.kind = pkGreedyAny
-  else:
-    result.kind = pkGreedyRep
-    result.sons = @[a]
-
-proc `!*`*(a: TPeg): TPeg {.rtl, extern: "npegsSearch".} =
-  ## constructs a "search" for the PEG `a`
-  result.kind = pkSearch
-  result.sons = @[a]
-
-proc `!*\`*(a: TPeg): TPeg {.rtl,
-                             extern: "npgegsCapturedSearch".} =
-  ## constructs a "captured search" for the PEG `a`
-  result.kind = pkCapturedSearch
-  result.sons = @[a]
-
-when false:
-  proc contains(a: TPeg, k: TPegKind): bool =
-    if a.kind == k: return true
-    case a.kind
-    of pkEmpty, pkAny, pkAnyRune, pkGreedyAny, pkNewLine, pkTerminal,
-       pkTerminalIgnoreCase, pkTerminalIgnoreStyle, pkChar, pkGreedyRepChar,
-       pkCharChoice, pkGreedyRepSet: discard
-    of pkNonTerminal: return true
-    else:
-      for i in 0..a.sons.len-1:
-        if contains(a.sons[i], k): return true
-
-proc `+`*(a: TPeg): TPeg {.rtl, extern: "npegsGreedyPosRep".} =
-  ## constructs a "greedy positive repetition" with the PEG `a`
-  return sequence(a, *a)
-
-proc `&`*(a: TPeg): TPeg {.rtl, extern: "npegsAndPredicate".} =
-  ## constructs an "and predicate" with the PEG `a`
-  result.kind = pkAndPredicate
-  result.sons = @[a]
-
-proc `!`*(a: TPeg): TPeg {.rtl, extern: "npegsNotPredicate".} =
-  ## constructs a "not predicate" with the PEG `a`
-  result.kind = pkNotPredicate
-  result.sons = @[a]
-
-proc any*: TPeg {.inline.} =
-  ## constructs the PEG `any character`:idx: (``.``)
-  result.kind = pkAny
-
-proc anyRune*: TPeg {.inline.} =
-  ## constructs the PEG `any rune`:idx: (``_``)
-  result.kind = pkAnyRune
-
-proc newLine*: TPeg {.inline.} =
-  ## constructs the PEG `newline`:idx: (``\n``)
-  result.kind = pkNewline
-
-proc UnicodeLetter*: TPeg {.inline.} =
-  ## constructs the PEG ``\letter`` which matches any Unicode letter.
-  result.kind = pkLetter
-
-proc UnicodeLower*: TPeg {.inline.} =
-  ## constructs the PEG ``\lower`` which matches any Unicode lowercase letter.
-  result.kind = pkLower
-
-proc UnicodeUpper*: TPeg {.inline.} =
-  ## constructs the PEG ``\upper`` which matches any Unicode lowercase letter.
-  result.kind = pkUpper
-
-proc UnicodeTitle*: TPeg {.inline.} =
-  ## constructs the PEG ``\title`` which matches any Unicode title letter.
-  result.kind = pkTitle
-
-proc UnicodeWhitespace*: TPeg {.inline.} =
-  ## constructs the PEG ``\white`` which matches any Unicode
-  ## whitespace character.
-  result.kind = pkWhitespace
-
-proc startAnchor*: TPeg {.inline.} =
-  ## constructs the PEG ``^`` which matches the start of the input.
-  result.kind = pkStartAnchor
-
-proc endAnchor*: TPeg {.inline.} =
-  ## constructs the PEG ``$`` which matches the end of the input.
-  result = !any()
-
-proc capture*(a: TPeg): TPeg {.rtl, extern: "npegsCapture".} =
-  ## constructs a capture with the PEG `a`
-  result.kind = pkCapture
-  result.sons = @[a]
-
-proc backref*(index: range[1..MaxSubPatterns]): TPeg {.
-  rtl, extern: "npegs$1".} =
-  ## constructs a back reference of the given `index`. `index` starts counting
-  ## from 1.
-  result.kind = pkBackRef
-  result.index = index-1
-
-proc backrefIgnoreCase*(index: range[1..MaxSubPatterns]): TPeg {.
-  rtl, extern: "npegs$1".} =
-  ## constructs a back reference of the given `index`. `index` starts counting
-  ## from 1. Ignores case for matching.
-  result.kind = pkBackRefIgnoreCase
-  result.index = index-1
-
-proc backrefIgnoreStyle*(index: range[1..MaxSubPatterns]): TPeg {.
-  rtl, extern: "npegs$1".}=
-  ## constructs a back reference of the given `index`. `index` starts counting
-  ## from 1. Ignores style for matching.
-  result.kind = pkBackRefIgnoreStyle
-  result.index = index-1
-
-proc spaceCost(n: TPeg): int =
-  case n.kind
-  of pkEmpty: discard
-  of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle, pkChar,
-     pkGreedyRepChar, pkCharChoice, pkGreedyRepSet,
-     pkAny..pkWhitespace, pkGreedyAny:
-    result = 1
-  of pkNonTerminal:
-    # we cannot inline a rule with a non-terminal
-    result = InlineThreshold+1
-  else:
-    for i in 0..n.len-1:
-      inc(result, spaceCost(n.sons[i]))
-      if result >= InlineThreshold: break
-
-proc nonterminal*(n: PNonTerminal): TPeg {.
-  rtl, extern: "npegs$1".} =
-  ## constructs a PEG that consists of the nonterminal symbol
-  assert n != nil
-  if ntDeclared in n.flags and spaceCost(n.rule) < InlineThreshold:
-    when false: echo "inlining symbol: ", n.name
-    result = n.rule # inlining of rule enables better optimizations
-  else:
-    result.kind = pkNonTerminal
-    result.nt = n
-
-proc newNonTerminal*(name: string, line, column: int): PNonTerminal {.
-  rtl, extern: "npegs$1".} =
-  ## constructs a nonterminal symbol
-  new(result)
-  result.name = name
-  result.line = line
-  result.col = column
-
-template letters*: TPeg =
-  ## expands to ``charset({'A'..'Z', 'a'..'z'})``
-  charset({'A'..'Z', 'a'..'z'})
-
-template digits*: TPeg =
-  ## expands to ``charset({'0'..'9'})``
-  charset({'0'..'9'})
-
-template whitespace*: TPeg =
-  ## expands to ``charset({' ', '\9'..'\13'})``
-  charset({' ', '\9'..'\13'})
-
-template identChars*: TPeg =
-  ## expands to ``charset({'a'..'z', 'A'..'Z', '0'..'9', '_'})``
-  charset({'a'..'z', 'A'..'Z', '0'..'9', '_'})
-
-template identStartChars*: TPeg =
-  ## expands to ``charset({'A'..'Z', 'a'..'z', '_'})``
-  charset({'a'..'z', 'A'..'Z', '_'})
-
-template ident*: TPeg =
-  ## same as ``[a-zA-Z_][a-zA-z_0-9]*``; standard identifier
-  sequence(charset({'a'..'z', 'A'..'Z', '_'}),
-           *charset({'a'..'z', 'A'..'Z', '0'..'9', '_'}))
-
-template natural*: TPeg =
-  ## same as ``\d+``
-  +digits
-
-# ------------------------- debugging -----------------------------------------
-
-proc esc(c: char, reserved = {'\0'..'\255'}): string =
-  case c
-  of '\b': result = "\\b"
-  of '\t': result = "\\t"
-  of '\c': result = "\\c"
-  of '\L': result = "\\l"
-  of '\v': result = "\\v"
-  of '\f': result = "\\f"
-  of '\e': result = "\\e"
-  of '\a': result = "\\a"
-  of '\\': result = "\\\\"
-  of 'a'..'z', 'A'..'Z', '0'..'9', '_': result = $c
-  elif c < ' ' or c >= '\128': result = '\\' & $ord(c)
-  elif c in reserved: result = '\\' & c
-  else: result = $c
-
-proc singleQuoteEsc(c: char): string = return "'" & esc(c, {'\''}) & "'"
-
-proc singleQuoteEsc(str: string): string =
-  result = "'"
-  for c in items(str): add result, esc(c, {'\''})
-  add result, '\''
-
-proc charSetEscAux(cc: set[char]): string =
-  const reserved = {'^', '-', ']'}
-  result = ""
-  var c1 = 0
-  while c1 <= 0xff:
-    if chr(c1) in cc:
-      var c2 = c1
-      while c2 < 0xff and chr(succ(c2)) in cc: inc(c2)
-      if c1 == c2:
-        add result, esc(chr(c1), reserved)
-      elif c2 == succ(c1):
-        add result, esc(chr(c1), reserved) & esc(chr(c2), reserved)
-      else:
-        add result, esc(chr(c1), reserved) & '-' & esc(chr(c2), reserved)
-      c1 = c2
-    inc(c1)
-
-proc charSetEsc(cc: set[char]): string =
-  if card(cc) >= 128+64:
-    result = "[^" & charSetEscAux({'\1'..'\xFF'} - cc) & ']'
-  else:
-    result = '[' & charSetEscAux(cc) & ']'
-
-proc toStrAux(r: TPeg, res: var string) =
-  case r.kind
-  of pkEmpty: add(res, "()")
-  of pkAny: add(res, '.')
-  of pkAnyRune: add(res, '_')
-  of pkLetter: add(res, "\\letter")
-  of pkLower: add(res, "\\lower")
-  of pkUpper: add(res, "\\upper")
-  of pkTitle: add(res, "\\title")
-  of pkWhitespace: add(res, "\\white")
-
-  of pkNewline: add(res, "\\n")
-  of pkTerminal: add(res, singleQuoteEsc(r.term))
-  of pkTerminalIgnoreCase:
-    add(res, 'i')
-    add(res, singleQuoteEsc(r.term))
-  of pkTerminalIgnoreStyle:
-    add(res, 'y')
-    add(res, singleQuoteEsc(r.term))
-  of pkChar: add(res, singleQuoteEsc(r.ch))
-  of pkCharChoice: add(res, charSetEsc(r.charChoice[]))
-  of pkNonTerminal: add(res, r.nt.name)
-  of pkSequence:
-    add(res, '(')
-    toStrAux(r.sons[0], res)
-    for i in 1 .. high(r.sons):
-      add(res, ' ')
-      toStrAux(r.sons[i], res)
-    add(res, ')')
-  of pkOrderedChoice:
-    add(res, '(')
-    toStrAux(r.sons[0], res)
-    for i in 1 .. high(r.sons):
-      add(res, " / ")
-      toStrAux(r.sons[i], res)
-    add(res, ')')
-  of pkGreedyRep:
-    toStrAux(r.sons[0], res)
-    add(res, '*')
-  of pkGreedyRepChar:
-    add(res, singleQuoteEsc(r.ch))
-    add(res, '*')
-  of pkGreedyRepSet:
-    add(res, charSetEsc(r.charChoice[]))
-    add(res, '*')
-  of pkGreedyAny:
-    add(res, ".*")
-  of pkOption:
-    toStrAux(r.sons[0], res)
-    add(res, '?')
-  of pkAndPredicate:
-    add(res, '&')
-    toStrAux(r.sons[0], res)
-  of pkNotPredicate:
-    add(res, '!')
-    toStrAux(r.sons[0], res)
-  of pkSearch:
-    add(res, '@')
-    toStrAux(r.sons[0], res)
-  of pkCapturedSearch:
-    add(res, "{@}")
-    toStrAux(r.sons[0], res)
-  of pkCapture:
-    add(res, '{')
-    toStrAux(r.sons[0], res)
-    add(res, '}')
-  of pkBackRef:
-    add(res, '$')
-    add(res, $r.index)
-  of pkBackRefIgnoreCase:
-    add(res, "i$")
-    add(res, $r.index)
-  of pkBackRefIgnoreStyle:
-    add(res, "y$")
-    add(res, $r.index)
-  of pkRule:
-    toStrAux(r.sons[0], res)
-    add(res, " <- ")
-    toStrAux(r.sons[1], res)
-  of pkList:
-    for i in 0 .. high(r.sons):
-      toStrAux(r.sons[i], res)
-      add(res, "\n")
-  of pkStartAnchor:
-    add(res, '^')
-
-proc `$` *(r: TPeg): string {.rtl, extern: "npegsToString".} =
-  ## converts a PEG to its string representation
-  result = ""
-  toStrAux(r, result)
-
-# --------------------- core engine -------------------------------------------
-
-type
-  TCaptures* {.final.} = object ## contains the captured substrings.
-    matches: array[0..MaxSubpatterns-1, tuple[first, last: int]]
-    ml: int
-    origStart: int
-
-proc bounds*(c: TCaptures,
-             i: range[0..MaxSubpatterns-1]): tuple[first, last: int] =
-  ## returns the bounds ``[first..last]`` of the `i`'th capture.
-  result = c.matches[i]
-
-when not useUnicode:
-  type
-    Rune = char
-  template fastRuneAt(s, i, ch) =
-    ch = s[i]
-    inc(i)
-  template runeLenAt(s, i): untyped = 1
-
-  proc isAlpha(a: char): bool {.inline.} = return a in {'a'..'z','A'..'Z'}
-  proc isUpper(a: char): bool {.inline.} = return a in {'A'..'Z'}
-  proc isLower(a: char): bool {.inline.} = return a in {'a'..'z'}
-  proc isTitle(a: char): bool {.inline.} = return false
-  proc isWhiteSpace(a: char): bool {.inline.} = return a in {' ', '\9'..'\13'}
-
-proc rawMatch*(s: string, p: TPeg, start: int, c: var TCaptures): int {.
-               rtl, extern: "npegs$1".} =
-  ## low-level matching proc that implements the PEG interpreter. Use this
-  ## for maximum efficiency (every other PEG operation ends up calling this
-  ## proc).
-  ## Returns -1 if it does not match, else the length of the match
-  case p.kind
-  of pkEmpty: result = 0 # match of length 0
-  of pkAny:
-    if s[start] != '\0': result = 1
-    else: result = -1
-  of pkAnyRune:
-    if s[start] != '\0':
-      result = runeLenAt(s, start)
-    else:
-      result = -1
-  of pkLetter:
-    if s[start] != '\0':
-      var a: Rune
-      result = start
-      fastRuneAt(s, result, a)
-      if isAlpha(a): dec(result, start)
-      else: result = -1
-    else:
-      result = -1
-  of pkLower:
-    if s[start] != '\0':
-      var a: Rune
-      result = start
-      fastRuneAt(s, result, a)
-      if isLower(a): dec(result, start)
-      else: result = -1
-    else:
-      result = -1
-  of pkUpper:
-    if s[start] != '\0':
-      var a: Rune
-      result = start
-      fastRuneAt(s, result, a)
-      if isUpper(a): dec(result, start)
-      else: result = -1
-    else:
-      result = -1
-  of pkTitle:
-    if s[start] != '\0':
-      var a: Rune
-      result = start
-      fastRuneAt(s, result, a)
-      if isTitle(a): dec(result, start)
-      else: result = -1
-    else:
-      result = -1
-  of pkWhitespace:
-    if s[start] != '\0':
-      var a: Rune
-      result = start
-      fastRuneAt(s, result, a)
-      if isWhitespace(a): dec(result, start)
-      else: result = -1
-    else:
-      result = -1
-  of pkGreedyAny:
-    result = len(s) - start
-  of pkNewLine:
-    if s[start] == '\L': result = 1
-    elif s[start] == '\C':
-      if s[start+1] == '\L': result = 2
-      else: result = 1
-    else: result = -1
-  of pkTerminal:
-    result = len(p.term)
-    for i in 0..result-1:
-      if p.term[i] != s[start+i]:
-        result = -1
-        break
-  of pkTerminalIgnoreCase:
-    var
-      i = 0
-      a, b: Rune
-    result = start
-    while i < len(p.term):
-      fastRuneAt(p.term, i, a)
-      fastRuneAt(s, result, b)
-      if toLower(a) != toLower(b):
-        result = -1
-        break
-    dec(result, start)
-  of pkTerminalIgnoreStyle:
-    var
-      i = 0
-      a, b: Rune
-    result = start
-    while i < len(p.term):
-      while true:
-        fastRuneAt(p.term, i, a)
-        if a != Rune('_'): break
-      while true:
-        fastRuneAt(s, result, b)
-        if b != Rune('_'): break
-      if toLower(a) != toLower(b):
-        result = -1
-        break
-    dec(result, start)
-  of pkChar:
-    if p.ch == s[start]: result = 1
-    else: result = -1
-  of pkCharChoice:
-    if contains(p.charChoice[], s[start]): result = 1
-    else: result = -1
-  of pkNonTerminal:
-    var oldMl = c.ml
-    when false: echo "enter: ", p.nt.name
-    result = rawMatch(s, p.nt.rule, start, c)
-    when false: echo "leave: ", p.nt.name
-    if result < 0: c.ml = oldMl
-  of pkSequence:
-    var oldMl = c.ml
-    result = 0
-    assert(not isNil(p.sons))
-    for i in 0..high(p.sons):
-      var x = rawMatch(s, p.sons[i], start+result, c)
-      if x < 0:
-        c.ml = oldMl
-        result = -1
-        break
-      else: inc(result, x)
-  of pkOrderedChoice:
-    var oldMl = c.ml
-    for i in 0..high(p.sons):
-      result = rawMatch(s, p.sons[i], start, c)
-      if result >= 0: break
-      c.ml = oldMl
-  of pkSearch:
-    var oldMl = c.ml
-    result = 0
-    while start+result < s.len:
-      var x = rawMatch(s, p.sons[0], start+result, c)
-      if x >= 0:
-        inc(result, x)
-        return
-      inc(result)
-    result = -1
-    c.ml = oldMl
-  of pkCapturedSearch:
-    var idx = c.ml # reserve a slot for the subpattern
-    inc(c.ml)
-    result = 0
-    while start+result < s.len:
-      var x = rawMatch(s, p.sons[0], start+result, c)
-      if x >= 0:
-        if idx < MaxSubpatterns:
-          c.matches[idx] = (start, start+result-1)
-        #else: silently ignore the capture
-        inc(result, x)
-        return
-      inc(result)
-    result = -1
-    c.ml = idx
-  of pkGreedyRep:
-    result = 0
-    while true:
-      var x = rawMatch(s, p.sons[0], start+result, c)
-      # if x == 0, we have an endless loop; so the correct behaviour would be
-      # not to break. But endless loops can be easily introduced:
-      # ``(comment / \w*)*`` is such an example. Breaking for x == 0 does the
-      # expected thing in this case.
-      if x <= 0: break
-      inc(result, x)
-  of pkGreedyRepChar:
-    result = 0
-    var ch = p.ch
-    while ch == s[start+result]: inc(result)
-  of pkGreedyRepSet:
-    result = 0
-    while contains(p.charChoice[], s[start+result]): inc(result)
-  of pkOption:
-    result = max(0, rawMatch(s, p.sons[0], start, c))
-  of pkAndPredicate:
-    var oldMl = c.ml
-    result = rawMatch(s, p.sons[0], start, c)
-    if result >= 0: result = 0 # do not consume anything
-    else: c.ml = oldMl
-  of pkNotPredicate:
-    var oldMl = c.ml
-    result = rawMatch(s, p.sons[0], start, c)
-    if result < 0: result = 0
-    else:
-      c.ml = oldMl
-      result = -1
-  of pkCapture:
-    var idx = c.ml # reserve a slot for the subpattern
-    inc(c.ml)
-    result = rawMatch(s, p.sons[0], start, c)
-    if result >= 0:
-      if idx < MaxSubpatterns:
-        c.matches[idx] = (start, start+result-1)
-      #else: silently ignore the capture
-    else:
-      c.ml = idx
-  of pkBackRef..pkBackRefIgnoreStyle:
-    if p.index >= c.ml: return -1
-    var (a, b) = c.matches[p.index]
-    var n: TPeg
-    n.kind = succ(pkTerminal, ord(p.kind)-ord(pkBackRef))
-    n.term = s.substr(a, b)
-    result = rawMatch(s, n, start, c)
-  of pkStartAnchor:
-    if c.origStart == start: result = 0
-    else: result = -1
-  of pkRule, pkList: assert false
-
-proc match*(s: string, pattern: TPeg, matches: var openarray[string],
-            start = 0): bool {.rtl, extern: "npegs$1Capture".} =
-  ## returns ``true`` if ``s[start..]`` matches the ``pattern`` and
-  ## the captured substrings in the array ``matches``. If it does not
-  ## match, nothing is written into ``matches`` and ``false`` is
-  ## returned.
-  var c: TCaptures
-  c.origStart = start
-  result = rawMatch(s, pattern, start, c) == len(s)-start
-  if result:
-    for i in 0..c.ml-1:
-      matches[i] = substr(s, c.matches[i][0], c.matches[i][1])
-
-proc match*(s: string, pattern: TPeg,
-            start = 0): bool {.rtl, extern: "npegs$1".} =
-  ## returns ``true`` if ``s`` matches the ``pattern`` beginning from ``start``.
-  var c: TCaptures
-  c.origStart = start
-  result = rawMatch(s, pattern, start, c) == len(s)-start
-
-proc matchLen*(s: string, pattern: TPeg, matches: var openarray[string],
-               start = 0): int {.rtl, extern: "npegs$1Capture".} =
-  ## the same as ``match``, but it returns the length of the match,
-  ## if there is no match, -1 is returned. Note that a match length
-  ## of zero can happen. It's possible that a suffix of `s` remains
-  ## that does not belong to the match.
-  var c: TCaptures
-  c.origStart = start
-  result = rawMatch(s, pattern, start, c)
-  if result >= 0:
-    for i in 0..c.ml-1:
-      matches[i] = substr(s, c.matches[i][0], c.matches[i][1])
-
-proc matchLen*(s: string, pattern: TPeg,
-               start = 0): int {.rtl, extern: "npegs$1".} =
-  ## the same as ``match``, but it returns the length of the match,
-  ## if there is no match, -1 is returned. Note that a match length
-  ## of zero can happen. It's possible that a suffix of `s` remains
-  ## that does not belong to the match.
-  var c: TCaptures
-  c.origStart = start
-  result = rawMatch(s, pattern, start, c)
-
-proc find*(s: string, pattern: TPeg, matches: var openarray[string],
-           start = 0): int {.rtl, extern: "npegs$1Capture".} =
-  ## returns the starting position of ``pattern`` in ``s`` and the captured
-  ## substrings in the array ``matches``. If it does not match, nothing
-  ## is written into ``matches`` and -1 is returned.
-  for i in start .. s.len-1:
-    if matchLen(s, pattern, matches, i) >= 0: return i
-  return -1
-  # could also use the pattern here: (!P .)* P
-
-proc findBounds*(s: string, pattern: TPeg, matches: var openarray[string],
-                 start = 0): tuple[first, last: int] {.
-                 rtl, extern: "npegs$1Capture".} =
-  ## returns the starting position and end position of ``pattern`` in ``s``
-  ## and the captured
-  ## substrings in the array ``matches``. If it does not match, nothing
-  ## is written into ``matches`` and (-1,0) is returned.
-  for i in start .. s.len-1:
-    var L = matchLen(s, pattern, matches, i)
-    if L >= 0: return (i, i+L-1)
-  return (-1, 0)
-
-proc find*(s: string, pattern: TPeg,
-           start = 0): int {.rtl, extern: "npegs$1".} =
-  ## returns the starting position of ``pattern`` in ``s``. If it does not
-  ## match, -1 is returned.
-  for i in start .. s.len-1:
-    if matchLen(s, pattern, i) >= 0: return i
-  return -1
-
-iterator findAll*(s: string, pattern: TPeg, start = 0): string =
-  ## yields all matching captures of pattern in `s`.
-  var matches: array[0..MaxSubpatterns-1, string]
-  var i = start
-  while i < s.len:
-    var L = matchLen(s, pattern, matches, i)
-    if L < 0: break
-    for k in 0..MaxSubPatterns-1:
-      if isNil(matches[k]): break
-      yield matches[k]
-    inc(i, L)
-
-proc findAll*(s: string, pattern: TPeg, start = 0): seq[string] {.
-  rtl, extern: "npegs$1".} =
-  ## returns all matching captures of pattern in `s`.
-  ## If it does not match, @[] is returned.
-  accumulateResult(findAll(s, pattern, start))
-
-template `=~`*(s: string, pattern: TPeg): untyped =
-  ## This calls ``match`` with an implicit declared ``matches`` array that
-  ## can be used in the scope of the ``=~`` call:
-  ##
-  ## .. code-block:: nim
-  ##
-  ##   if line =~ peg"\s* {\w+} \s* '=' \s* {\w+}":
-  ##     # matches a key=value pair:
-  ##     echo("Key: ", matches[0])
-  ##     echo("Value: ", matches[1])
-  ##   elif line =~ peg"\s*{'#'.*}":
-  ##     # matches a comment
-  ##     # note that the implicit ``matches`` array is different from the
-  ##     # ``matches`` array of the first branch
-  ##     echo("comment: ", matches[0])
-  ##   else:
-  ##     echo("syntax error")
-  ##
-  when not declaredInScope(matches):
-    var matches {.inject.}: array[0..MaxSubpatterns-1, string]
-  match(s, pattern, matches)
-
-# ------------------------- more string handling ------------------------------
-
-proc contains*(s: string, pattern: TPeg, start = 0): bool {.
-  rtl, extern: "npegs$1".} =
-  ## same as ``find(s, pattern, start) >= 0``
-  return find(s, pattern, start) >= 0
-
-proc contains*(s: string, pattern: TPeg, matches: var openArray[string],
-              start = 0): bool {.rtl, extern: "npegs$1Capture".} =
-  ## same as ``find(s, pattern, matches, start) >= 0``
-  return find(s, pattern, matches, start) >= 0
-
-proc startsWith*(s: string, prefix: TPeg, start = 0): bool {.
-  rtl, extern: "npegs$1".} =
-  ## returns true if `s` starts with the pattern `prefix`
-  result = matchLen(s, prefix, start) >= 0
-
-proc endsWith*(s: string, suffix: TPeg, start = 0): bool {.
-  rtl, extern: "npegs$1".} =
-  ## returns true if `s` ends with the pattern `prefix`
-  for i in start .. s.len-1:
-    if matchLen(s, suffix, i) == s.len - i: return true
-
-proc replacef*(s: string, sub: TPeg, by: string): string {.
-  rtl, extern: "npegs$1".} =
-  ## Replaces `sub` in `s` by the string `by`. Captures can be accessed in `by`
-  ## with the notation ``$i`` and ``$#`` (see strutils.`%`). Examples:
-  ##
-  ## .. code-block:: nim
-  ##   "var1=key; var2=key2".replace(peg"{\ident}'='{\ident}", "$1<-$2$2")
-  ##
-  ## Results in:
-  ##
-  ## .. code-block:: nim
-  ##
-  ##   "var1<-keykey; val2<-key2key2"
-  result = ""
-  var i = 0
-  var caps: array[0..MaxSubpatterns-1, string]
-  while i < s.len:
-    var x = matchLen(s, sub, caps, i)
-    if x <= 0:
-      add(result, s[i])
-      inc(i)
-    else:
-      addf(result, by, caps)
-      inc(i, x)
-  add(result, substr(s, i))
-
-proc replace*(s: string, sub: TPeg, by = ""): string {.
-  rtl, extern: "npegs$1".} =
-  ## Replaces `sub` in `s` by the string `by`. Captures cannot be accessed
-  ## in `by`.
-  result = ""
-  var i = 0
-  var caps: array[0..MaxSubpatterns-1, string]
-  while i < s.len:
-    var x = matchLen(s, sub, caps, i)
-    if x <= 0:
-      add(result, s[i])
-      inc(i)
-    else:
-      addf(result, by, caps)
-      inc(i, x)
-  add(result, substr(s, i))
-
-proc parallelReplace*(s: string, subs: varargs[
-                      tuple[pattern: TPeg, repl: string]]): string {.
-                      rtl, extern: "npegs$1".} =
-  ## Returns a modified copy of `s` with the substitutions in `subs`
-  ## applied in parallel.
-  result = ""
-  var i = 0
-  var caps: array[0..MaxSubpatterns-1, string]
-  while i < s.len:
-    block searchSubs:
-      for j in 0..high(subs):
-        var x = matchLen(s, subs[j][0], caps, i)
-        if x > 0:
-          addf(result, subs[j][1], caps)
-          inc(i, x)
-          break searchSubs
-      add(result, s[i])
-      inc(i)
-  # copy the rest:
-  add(result, substr(s, i))
-
-proc transformFile*(infile, outfile: string,
-                    subs: varargs[tuple[pattern: TPeg, repl: string]]) {.
-                    rtl, extern: "npegs$1".} =
-  ## reads in the file `infile`, performs a parallel replacement (calls
-  ## `parallelReplace`) and writes back to `outfile`. Calls ``quit`` if an
-  ## error occurs. This is supposed to be used for quick scripting.
-  var x = readFile(infile)
-  if not isNil(x):
-    var f: File
-    if open(f, outfile, fmWrite):
-      write(f, x.parallelReplace(subs))
-      close(f)
-    else:
-      quit("cannot open for writing: " & outfile)
-  else:
-    quit("cannot open for reading: " & infile)
-
-iterator split*(s: string, sep: TPeg): string =
-  ## Splits the string `s` into substrings.
-  ##
-  ## Substrings are separated by the PEG `sep`.
-  ## Examples:
-  ##
-  ## .. code-block:: nim
-  ##   for word in split("00232this02939is39an22example111", peg"\d+"):
-  ##     writeLine(stdout, word)
-  ##
-  ## Results in:
-  ##
-  ## .. code-block:: nim
-  ##   "this"
-  ##   "is"
-  ##   "an"
-  ##   "example"
-  ##
-  var
-    first = 0
-    last = 0
-  while last < len(s):
-    var x = matchLen(s, sep, last)
-    if x > 0: inc(last, x)
-    first = last
-    while last < len(s):
-      inc(last)
-      x = matchLen(s, sep, last)
-      if x > 0: break
-    if first < last:
-      yield substr(s, first, last-1)
-
-proc split*(s: string, sep: TPeg): seq[string] {.
-  rtl, extern: "npegs$1".} =
-  ## Splits the string `s` into substrings.
-  accumulateResult(split(s, sep))
-
-# ------------------- scanner -------------------------------------------------
-
-type
-  TModifier = enum
-    modNone,
-    modVerbatim,
-    modIgnoreCase,
-    modIgnoreStyle
-  TTokKind = enum       ## enumeration of all tokens
-    tkInvalid,          ## invalid token
-    tkEof,              ## end of file reached
-    tkAny,              ## .
-    tkAnyRune,          ## _
-    tkIdentifier,       ## abc
-    tkStringLit,        ## "abc" or 'abc'
-    tkCharSet,          ## [^A-Z]
-    tkParLe,            ## '('
-    tkParRi,            ## ')'
-    tkCurlyLe,          ## '{'
-    tkCurlyRi,          ## '}'
-    tkCurlyAt,          ## '{@}'
-    tkArrow,            ## '<-'
-    tkBar,              ## '/'
-    tkStar,             ## '*'
-    tkPlus,             ## '+'
-    tkAmp,              ## '&'
-    tkNot,              ## '!'
-    tkOption,           ## '?'
-    tkAt,               ## '@'
-    tkBuiltin,          ## \identifier
-    tkEscaped,          ## \\
-    tkBackref,          ## '$'
-    tkDollar,           ## '$'
-    tkHat               ## '^'
-
-  TToken {.final.} = object  ## a token
-    kind: TTokKind           ## the type of the token
-    modifier: TModifier
-    literal: string          ## the parsed (string) literal
-    charset: set[char]       ## if kind == tkCharSet
-    index: int               ## if kind == tkBackref
-
-  TPegLexer {.inheritable.} = object          ## the lexer object.
-    bufpos: int               ## the current position within the buffer
-    buf: cstring              ## the buffer itself
-    lineNumber: int           ## the current line number
-    lineStart: int            ## index of last line start in buffer
-    colOffset: int            ## column to add
-    filename: string
-
-const
-  tokKindToStr: array[TTokKind, string] = [
-    "invalid", "[EOF]", ".", "_", "identifier", "string literal",
-    "character set", "(", ")", "{", "}", "{@}",
-    "<-", "/", "*", "+", "&", "!", "?",
-    "@", "built-in", "escaped", "$", "$", "^"
-  ]
-
-proc HandleCR(L: var TPegLexer, pos: int): int =
-  assert(L.buf[pos] == '\c')
-  inc(L.linenumber)
-  result = pos+1
-  if L.buf[result] == '\L': inc(result)
-  L.lineStart = result
-
-proc HandleLF(L: var TPegLexer, pos: int): int =
-  assert(L.buf[pos] == '\L')
-  inc(L.linenumber)
-  result = pos+1
-  L.lineStart = result
-
-proc init(L: var TPegLexer, input, filename: string, line = 1, col = 0) =
-  L.buf = input
-  L.bufpos = 0
-  L.lineNumber = line
-  L.colOffset = col
-  L.lineStart = 0
-  L.filename = filename
-
-proc getColumn(L: TPegLexer): int {.inline.} =
-  result = abs(L.bufpos - L.lineStart) + L.colOffset
-
-proc getLine(L: TPegLexer): int {.inline.} =
-  result = L.linenumber
-
-proc errorStr(L: TPegLexer, msg: string, line = -1, col = -1): string =
-  var line = if line < 0: getLine(L) else: line
-  var col = if col < 0: getColumn(L) else: col
-  result = "$1($2, $3) Error: $4" % [L.filename, $line, $col, msg]
-
-proc handleHexChar(c: var TPegLexer, xi: var int) =
-  case c.buf[c.bufpos]
-  of '0'..'9':
-    xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('0'))
-    inc(c.bufpos)
-  of 'a'..'f':
-    xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('a') + 10)
-    inc(c.bufpos)
-  of 'A'..'F':
-    xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('A') + 10)
-    inc(c.bufpos)
-  else: discard
-
-proc getEscapedChar(c: var TPegLexer, tok: var TToken) =
-  inc(c.bufpos)
-  case c.buf[c.bufpos]
-  of 'r', 'R', 'c', 'C':
-    add(tok.literal, '\c')
-    inc(c.bufpos)
-  of 'l', 'L':
-    add(tok.literal, '\L')
-    inc(c.bufpos)
-  of 'f', 'F':
-    add(tok.literal, '\f')
-    inc(c.bufpos)
-  of 'e', 'E':
-    add(tok.literal, '\e')
-    inc(c.bufpos)
-  of 'a', 'A':
-    add(tok.literal, '\a')
-    inc(c.bufpos)
-  of 'b', 'B':
-    add(tok.literal, '\b')
-    inc(c.bufpos)
-  of 'v', 'V':
-    add(tok.literal, '\v')
-    inc(c.bufpos)
-  of 't', 'T':
-    add(tok.literal, '\t')
-    inc(c.bufpos)
-  of 'x', 'X':
-    inc(c.bufpos)
-    var xi = 0
-    handleHexChar(c, xi)
-    handleHexChar(c, xi)
-    if xi == 0: tok.kind = tkInvalid
-    else: add(tok.literal, chr(xi))
-  of '0'..'9':
-    var val = ord(c.buf[c.bufpos]) - ord('0')
-    inc(c.bufpos)
-    var i = 1
-    while (i <= 3) and (c.buf[c.bufpos] in {'0'..'9'}):
-      val = val * 10 + ord(c.buf[c.bufpos]) - ord('0')
-      inc(c.bufpos)
-      inc(i)
-    if val > 0 and val <= 255: add(tok.literal, chr(val))
-    else: tok.kind = tkInvalid
-  of '\0'..'\31':
-    tok.kind = tkInvalid
-  elif c.buf[c.bufpos] in strutils.Letters:
-    tok.kind = tkInvalid
-  else:
-    add(tok.literal, c.buf[c.bufpos])
-    inc(c.bufpos)
-
-proc skip(c: var TPegLexer) =
-  var pos = c.bufpos
-  var buf = c.buf
-  while true:
-    case buf[pos]
-    of ' ', '\t':
-      inc(pos)
-    of '#':
-      while not (buf[pos] in {'\c', '\L', '\0'}): inc(pos)
-    of '\c':
-      pos = HandleCR(c, pos)
-      buf = c.buf
-    of '\L':
-      pos = HandleLF(c, pos)
-      buf = c.buf
-    else:
-      break                   # EndOfFile also leaves the loop
-  c.bufpos = pos
-
-proc getString(c: var TPegLexer, tok: var TToken) =
-  tok.kind = tkStringLit
-  var pos = c.bufPos + 1
-  var buf = c.buf
-  var quote = buf[pos-1]
-  while true:
-    case buf[pos]
-    of '\\':
-      c.bufpos = pos
-      getEscapedChar(c, tok)
-      pos = c.bufpos
-    of '\c', '\L', '\0':
-      tok.kind = tkInvalid
-      break
-    elif buf[pos] == quote:
-      inc(pos)
-      break
-    else:
-      add(tok.literal, buf[pos])
-      inc(pos)
-  c.bufpos = pos
-
-proc getDollar(c: var TPegLexer, tok: var TToken) =
-  var pos = c.bufPos + 1
-  var buf = c.buf
-  if buf[pos] in {'0'..'9'}:
-    tok.kind = tkBackref
-    tok.index = 0
-    while buf[pos] in {'0'..'9'}:
-      tok.index = tok.index * 10 + ord(buf[pos]) - ord('0')
-      inc(pos)
-  else:
-    tok.kind = tkDollar
-  c.bufpos = pos
-
-proc getCharSet(c: var TPegLexer, tok: var TToken) =
-  tok.kind = tkCharSet
-  tok.charset = {}
-  var pos = c.bufPos + 1
-  var buf = c.buf
-  var caret = false
-  if buf[pos] == '^':
-    inc(pos)
-    caret = true
-  while true:
-    var ch: char
-    case buf[pos]
-    of ']':
-      inc(pos)
-      break
-    of '\\':
-      c.bufpos = pos
-      getEscapedChar(c, tok)
-      pos = c.bufpos
-      ch = tok.literal[tok.literal.len-1]
-    of '\C', '\L', '\0':
-      tok.kind = tkInvalid
-      break
-    else:
-      ch = buf[pos]
-      inc(pos)
-    incl(tok.charset, ch)
-    if buf[pos] == '-':
-      if buf[pos+1] == ']':
-        incl(tok.charset, '-')
-        inc(pos)
-      else:
-        inc(pos)
-        var ch2: char
-        case buf[pos]
-        of '\\':
-          c.bufpos = pos
-          getEscapedChar(c, tok)
-          pos = c.bufpos
-          ch2 = tok.literal[tok.literal.len-1]
-        of '\C', '\L', '\0':
-          tok.kind = tkInvalid
-          break
-        else:
-          ch2 = buf[pos]
-          inc(pos)
-        for i in ord(ch)+1 .. ord(ch2):
-          incl(tok.charset, chr(i))
-  c.bufpos = pos
-  if caret: tok.charset = {'\1'..'\xFF'} - tok.charset
-
-proc getSymbol(c: var TPegLexer, tok: var TToken) =
-  var pos = c.bufpos
-  var buf = c.buf
-  while true:
-    add(tok.literal, buf[pos])
-    inc(pos)
-    if buf[pos] notin strutils.IdentChars: break
-  c.bufpos = pos
-  tok.kind = tkIdentifier
-
-proc getBuiltin(c: var TPegLexer, tok: var TToken) =
-  if c.buf[c.bufpos+1] in strutils.Letters:
-    inc(c.bufpos)
-    getSymbol(c, tok)
-    tok.kind = tkBuiltin
-  else:
-    tok.kind = tkEscaped
-    getEscapedChar(c, tok) # may set tok.kind to tkInvalid
-
-proc getTok(c: var TPegLexer, tok: var TToken) =
-  tok.kind = tkInvalid
-  tok.modifier = modNone
-  setlen(tok.literal, 0)
-  skip(c)
-  case c.buf[c.bufpos]
-  of '{':
-    inc(c.bufpos)
-    if c.buf[c.bufpos] == '@' and c.buf[c.bufpos+1] == '}':
-      tok.kind = tkCurlyAt
-      inc(c.bufpos, 2)
-      add(tok.literal, "{@}")
-    else:
-      tok.kind = tkCurlyLe
-      add(tok.literal, '{')
-  of '}':
-    tok.kind = tkCurlyRi
-    inc(c.bufpos)
-    add(tok.literal, '}')
-  of '[':
-    getCharset(c, tok)
-  of '(':
-    tok.kind = tkParLe
-    inc(c.bufpos)
-    add(tok.literal, '(')
-  of ')':
-    tok.kind = tkParRi
-    inc(c.bufpos)
-    add(tok.literal, ')')
-  of '.':
-    tok.kind = tkAny
-    inc(c.bufpos)
-    add(tok.literal, '.')
-  of '_':
-    tok.kind = tkAnyRune
-    inc(c.bufpos)
-    add(tok.literal, '_')
-  of '\\':
-    getBuiltin(c, tok)
-  of '\'', '"': getString(c, tok)
-  of '$': getDollar(c, tok)
-  of '\0':
-    tok.kind = tkEof
-    tok.literal = "[EOF]"
-  of 'a'..'z', 'A'..'Z', '\128'..'\255':
-    getSymbol(c, tok)
-    if c.buf[c.bufpos] in {'\'', '"'} or
-        c.buf[c.bufpos] == '$' and c.buf[c.bufpos+1] in {'0'..'9'}:
-      case tok.literal
-      of "i": tok.modifier = modIgnoreCase
-      of "y": tok.modifier = modIgnoreStyle
-      of "v": tok.modifier = modVerbatim
-      else: discard
-      setLen(tok.literal, 0)
-      if c.buf[c.bufpos] == '$':
-        getDollar(c, tok)
-      else:
-        getString(c, tok)
-      if tok.modifier == modNone: tok.kind = tkInvalid
-  of '+':
-    tok.kind = tkPlus
-    inc(c.bufpos)
-    add(tok.literal, '+')
-  of '*':
-    tok.kind = tkStar
-    inc(c.bufpos)
-    add(tok.literal, '+')
-  of '<':
-    if c.buf[c.bufpos+1] == '-':
-      inc(c.bufpos, 2)
-      tok.kind = tkArrow
-      add(tok.literal, "<-")
-    else:
-      add(tok.literal, '<')
-  of '/':
-    tok.kind = tkBar
-    inc(c.bufpos)
-    add(tok.literal, '/')
-  of '?':
-    tok.kind = tkOption
-    inc(c.bufpos)
-    add(tok.literal, '?')
-  of '!':
-    tok.kind = tkNot
-    inc(c.bufpos)
-    add(tok.literal, '!')
-  of '&':
-    tok.kind = tkAmp
-    inc(c.bufpos)
-    add(tok.literal, '!')
-  of '@':
-    tok.kind = tkAt
-    inc(c.bufpos)
-    add(tok.literal, '@')
-    if c.buf[c.bufpos] == '@':
-      tok.kind = tkCurlyAt
-      inc(c.bufpos)
-      add(tok.literal, '@')
-  of '^':
-    tok.kind = tkHat
-    inc(c.bufpos)
-    add(tok.literal, '^')
-  else:
-    add(tok.literal, c.buf[c.bufpos])
-    inc(c.bufpos)
-
-proc arrowIsNextTok(c: TPegLexer): bool =
-  # the only look ahead we need
-  var pos = c.bufpos
-  while c.buf[pos] in {'\t', ' '}: inc(pos)
-  result = c.buf[pos] == '<' and c.buf[pos+1] == '-'
-
-# ----------------------------- parser ----------------------------------------
-
-type
-  EInvalidPeg* = object of ValueError ## raised if an invalid
-                                      ## PEG has been detected
-  TPegParser = object of TPegLexer ## the PEG parser object
-    tok: TToken
-    nonterms: seq[PNonTerminal]
-    modifier: TModifier
-    captures: int
-    identIsVerbatim: bool
-    skip: TPeg
-
-proc pegError(p: TPegParser, msg: string, line = -1, col = -1) =
-  var e: ref EInvalidPeg
-  new(e)
-  e.msg = errorStr(p, msg, line, col)
-  raise e
-
-proc getTok(p: var TPegParser) =
-  getTok(p, p.tok)
-  if p.tok.kind == tkInvalid: pegError(p, "invalid token")
-
-proc eat(p: var TPegParser, kind: TTokKind) =
-  if p.tok.kind == kind: getTok(p)
-  else: pegError(p, tokKindToStr[kind] & " expected")
-
-proc parseExpr(p: var TPegParser): TPeg
-
-proc getNonTerminal(p: var TPegParser, name: string): PNonTerminal =
-  for i in 0..high(p.nonterms):
-    result = p.nonterms[i]
-    if cmpIgnoreStyle(result.name, name) == 0: return
-  # forward reference:
-  result = newNonTerminal(name, getLine(p), getColumn(p))
-  add(p.nonterms, result)
-
-proc modifiedTerm(s: string, m: TModifier): TPeg =
-  case m
-  of modNone, modVerbatim: result = term(s)
-  of modIgnoreCase: result = termIgnoreCase(s)
-  of modIgnoreStyle: result = termIgnoreStyle(s)
-
-proc modifiedBackref(s: int, m: TModifier): TPeg =
-  case m
-  of modNone, modVerbatim: result = backRef(s)
-  of modIgnoreCase: result = backRefIgnoreCase(s)
-  of modIgnoreStyle: result = backRefIgnoreStyle(s)
-
-proc builtin(p: var TPegParser): TPeg =
-  # do not use "y", "skip" or "i" as these would be ambiguous
-  case p.tok.literal
-  of "n": result = newLine()
-  of "d": result = charset({'0'..'9'})
-  of "D": result = charset({'\1'..'\xff'} - {'0'..'9'})
-  of "s": result = charset({' ', '\9'..'\13'})
-  of "S": result = charset({'\1'..'\xff'} - {' ', '\9'..'\13'})
-  of "w": result = charset({'a'..'z', 'A'..'Z', '_', '0'..'9'})
-  of "W": result = charset({'\1'..'\xff'} - {'a'..'z','A'..'Z','_','0'..'9'})
-  of "a": result = charset({'a'..'z', 'A'..'Z'})
-  of "A": result = charset({'\1'..'\xff'} - {'a'..'z', 'A'..'Z'})
-  of "ident": result = tpegs.ident
-  of "letter": result = UnicodeLetter()
-  of "upper": result = UnicodeUpper()
-  of "lower": result = UnicodeLower()
-  of "title": result = UnicodeTitle()
-  of "white": result = UnicodeWhitespace()
-  else: pegError(p, "unknown built-in: " & p.tok.literal)
-
-proc token(terminal: TPeg, p: TPegParser): TPeg =
-  if p.skip.kind == pkEmpty: result = terminal
-  else: result = sequence(p.skip, terminal)
-
-proc primary(p: var TPegParser): TPeg =
-  case p.tok.kind
-  of tkAmp:
-    getTok(p)
-    return &primary(p)
-  of tkNot:
-    getTok(p)
-    return !primary(p)
-  of tkAt:
-    getTok(p)
-    return !*primary(p)
-  of tkCurlyAt:
-    getTok(p)
-    return !*\primary(p).token(p)
-  else: discard
-  case p.tok.kind
-  of tkIdentifier:
-    if p.identIsVerbatim:
-      var m = p.tok.modifier
-      if m == modNone: m = p.modifier
-      result = modifiedTerm(p.tok.literal, m).token(p)
-      getTok(p)
-    elif not arrowIsNextTok(p):
-      var nt = getNonTerminal(p, p.tok.literal)
-      incl(nt.flags, ntUsed)
-      result = nonTerminal(nt).token(p)
-      getTok(p)
-    else:
-      pegError(p, "expression expected, but found: " & p.tok.literal)
-  of tkStringLit:
-    var m = p.tok.modifier
-    if m == modNone: m = p.modifier
-    result = modifiedTerm(p.tok.literal, m).token(p)
-    getTok(p)
-  of tkCharSet:
-    if '\0' in p.tok.charset:
-      pegError(p, "binary zero ('\\0') not allowed in character class")
-    result = charset(p.tok.charset).token(p)
-    getTok(p)
-  of tkParLe:
-    getTok(p)
-    result = parseExpr(p)
-    eat(p, tkParRi)
-  of tkCurlyLe:
-    getTok(p)
-    result = capture(parseExpr(p)).token(p)
-    eat(p, tkCurlyRi)
-    inc(p.captures)
-  of tkAny:
-    result = any().token(p)
-    getTok(p)
-  of tkAnyRune:
-    result = anyRune().token(p)
-    getTok(p)
-  of tkBuiltin:
-    result = builtin(p).token(p)
-    getTok(p)
-  of tkEscaped:
-    result = term(p.tok.literal[0]).token(p)
-    getTok(p)
-  of tkDollar:
-    result = endAnchor()
-    getTok(p)
-  of tkHat:
-    result = startAnchor()
-    getTok(p)
-  of tkBackref:
-    var m = p.tok.modifier
-    if m == modNone: m = p.modifier
-    result = modifiedBackRef(p.tok.index, m).token(p)
-    if p.tok.index < 0 or p.tok.index > p.captures:
-      pegError(p, "invalid back reference index: " & $p.tok.index)
-    getTok(p)
-  else:
-    pegError(p, "expression expected, but found: " & p.tok.literal)
-    getTok(p) # we must consume a token here to prevent endless loops!
-  while true:
-    case p.tok.kind
-    of tkOption:
-      result = ?result
-      getTok(p)
-    of tkStar:
-      result = *result
-      getTok(p)
-    of tkPlus:
-      result = +result
-      getTok(p)
-    else: break
-
-proc seqExpr(p: var TPegParser): TPeg =
-  result = primary(p)
-  while true:
-    case p.tok.kind
-    of tkAmp, tkNot, tkAt, tkStringLit, tkCharset, tkParLe, tkCurlyLe,
-       tkAny, tkAnyRune, tkBuiltin, tkEscaped, tkDollar, tkBackref,
-       tkHat, tkCurlyAt:
-      result = sequence(result, primary(p))
-    of tkIdentifier:
-      if not arrowIsNextTok(p):
-        result = sequence(result, primary(p))
-      else: break
-    else: break
-
-proc parseExpr(p: var TPegParser): TPeg =
-  result = seqExpr(p)
-  while p.tok.kind == tkBar:
-    getTok(p)
-    result = result / seqExpr(p)
-
-proc parseRule(p: var TPegParser): PNonTerminal =
-  if p.tok.kind == tkIdentifier and arrowIsNextTok(p):
-    result = getNonTerminal(p, p.tok.literal)
-    if ntDeclared in result.flags:
-      pegError(p, "attempt to redefine: " & result.name)
-    result.line = getLine(p)
-    result.col = getColumn(p)
-    getTok(p)
-    eat(p, tkArrow)
-    result.rule = parseExpr(p)
-    incl(result.flags, ntDeclared) # NOW inlining may be attempted
-  else:
-    pegError(p, "rule expected, but found: " & p.tok.literal)
-
-proc rawParse(p: var TPegParser): TPeg =
-  ## parses a rule or a PEG expression
-  while p.tok.kind == tkBuiltin:
-    case p.tok.literal
-    of "i":
-      p.modifier = modIgnoreCase
-      getTok(p)
-    of "y":
-      p.modifier = modIgnoreStyle
-      getTok(p)
-    of "skip":
-      getTok(p)
-      p.skip = ?primary(p)
-    else: break
-  if p.tok.kind == tkIdentifier and arrowIsNextTok(p):
-    result = parseRule(p).rule
-    while p.tok.kind != tkEof:
-      discard parseRule(p)
-  else:
-    p.identIsVerbatim = true
-    result = parseExpr(p)
-  if p.tok.kind != tkEof:
-    pegError(p, "EOF expected, but found: " & p.tok.literal)
-  for i in 0..high(p.nonterms):
-    var nt = p.nonterms[i]
-    if ntDeclared notin nt.flags:
-      pegError(p, "undeclared identifier: " & nt.name, nt.line, nt.col)
-    elif ntUsed notin nt.flags and i > 0:
-      pegError(p, "unused rule: " & nt.name, nt.line, nt.col)
-
-proc parsePeg*(pattern: string, filename = "pattern", line = 1, col = 0): TPeg =
-  ## constructs a TPeg object from `pattern`. `filename`, `line`, `col` are
-  ## used for error messages, but they only provide start offsets. `parsePeg`
-  ## keeps track of line and column numbers within `pattern`.
-  var p: TPegParser
-  init(TPegLexer(p), pattern, filename, line, col)
-  p.tok.kind = tkInvalid
-  p.tok.modifier = modNone
-  p.tok.literal = ""
-  p.tok.charset = {}
-  p.nonterms = @[]
-  p.identIsVerbatim = false
-  getTok(p)
-  result = rawParse(p)
-
-proc peg*(pattern: string): TPeg =
-  ## constructs a TPeg object from the `pattern`. The short name has been
-  ## chosen to encourage its use as a raw string modifier::
-  ##
-  ##   peg"{\ident} \s* '=' \s* {.*}"
-  result = parsePeg(pattern, "pattern")
-
-proc escapePeg*(s: string): string =
-  ## escapes `s` so that it is matched verbatim when used as a peg.
-  result = ""
-  var inQuote = false
-  for c in items(s):
-    case c
-    of '\0'..'\31', '\'', '"', '\\':
-      if inQuote:
-        result.add('\'')
-        inQuote = false
-      result.add("\\x")
-      result.add(toHex(ord(c), 2))
-    else:
-      if not inQuote:
-        result.add('\'')
-        inQuote = true
-      result.add(c)
-  if inQuote: result.add('\'')
-
-when isMainModule:
-  doAssert escapePeg("abc''def'") == r"'abc'\x27\x27'def'\x27"
-  #doAssert match("(a b c)", peg"'(' @ ')'")
-  doAssert match("W_HI_Le", peg"\y 'while'")
-  doAssert(not match("W_HI_L", peg"\y 'while'"))
-  doAssert(not match("W_HI_Le", peg"\y v'while'"))
-  doAssert match("W_HI_Le", peg"y'while'")
-
-  doAssert($ +digits == $peg"\d+")
-  doAssert "0158787".match(peg"\d+")
-  doAssert "ABC 0232".match(peg"\w+\s+\d+")
-  doAssert "ABC".match(peg"\d+ / \w+")
-
-  for word in split("00232this02939is39an22example111", peg"\d+"):
-    writeLine(stdout, word)
-
-  doAssert matchLen("key", ident) == 3
-
-  var pattern = sequence(ident, *whitespace, term('='), *whitespace, ident)
-  doAssert matchLen("key1=  cal9", pattern) == 11
-
-  var ws = newNonTerminal("ws", 1, 1)
-  ws.rule = *whitespace
-
-  var expr = newNonTerminal("expr", 1, 1)
-  expr.rule = sequence(capture(ident), *sequence(
-                nonterminal(ws), term('+'), nonterminal(ws), nonterminal(expr)))
-
-  var c: TCaptures
-  var s = "a+b +  c +d+e+f"
-  doAssert rawMatch(s, expr.rule, 0, c) == len(s)
-  var a = ""
-  for i in 0..c.ml-1:
-    a.add(substr(s, c.matches[i][0], c.matches[i][1]))
-  doAssert a == "abcdef"
-  #echo expr.rule
-
-  #const filename = "lib/devel/peg/grammar.txt"
-  #var grammar = parsePeg(newFileStream(filename, fmRead), filename)
-  #echo "a <- [abc]*?".match(grammar)
-  doAssert find("_____abc_______", term("abc"), 2) == 5
-  doAssert match("_______ana", peg"A <- 'ana' / . A")
-  doAssert match("abcs%%%", peg"A <- ..A / .A / '%'")
-
-  if "abc" =~ peg"{'a'}'bc' 'xyz' / {\ident}":
-    doAssert matches[0] == "abc"
-  else:
-    doAssert false
-
-  var g2 = peg"""S <- A B / C D
-                 A <- 'a'+
-                 B <- 'b'+
-                 C <- 'c'+
-                 D <- 'd'+
-              """
-  doAssert($g2 == "((A B) / (C D))")
-  doAssert match("cccccdddddd", g2)
-  doAssert("var1=key; var2=key2".replacef(peg"{\ident}'='{\ident}", "$1<-$2$2") ==
-         "var1<-keykey; var2<-key2key2")
-  doAssert "var1=key; var2=key2".endsWith(peg"{\ident}'='{\ident}")
-
-  if "aaaaaa" =~ peg"'aa' !. / ({'a'})+":
-    doAssert matches[0] == "a"
-  else:
-    doAssert false
-
-  block:
-    var matches: array[0..2, string]
-    if match("abcdefg", peg"c {d} ef {g}", matches, 2):
-      doAssert matches[0] == "d"
-      doAssert matches[1] == "g"
-    else:
-      doAssert false
-
-  for x in findAll("abcdef", peg"{.}", 3):
-    echo x
-
-  if "f(a, b)" =~ peg"{[0-9]+} / ({\ident} '(' {@} ')')":
-    doAssert matches[0] == "f"
-    doAssert matches[1] == "a, b"
-  else:
-    doAssert false
-
-  doAssert match("eine übersicht und außerdem", peg"(\letter \white*)+")
-  # ß is not a lower cased letter?!
-  doAssert match("eine übersicht und auerdem", peg"(\lower \white*)+")
-  doAssert match("EINE ÜBERSICHT UND AUSSERDEM", peg"(\upper \white*)+")
-  doAssert(not match("456678", peg"(\letter)+"))
-
-  doAssert("var1 = key; var2 = key2".replacef(
-    peg"\skip(\s*) {\ident}'='{\ident}", "$1<-$2$2") ==
-         "var1<-keykey;var2<-key2key2")
-
-  doAssert match("prefix/start", peg"^start$", 7)
-
-  # tricky test to check for false aliasing:
-  block:
-    var a = term"key"
-    echo($sequence(sequence(a, term"value"), *a))
-
diff --git a/tests/stdlib/ttimes.nim b/tests/stdlib/ttimes.nim
index f35965286..4ab3ba581 100644
--- a/tests/stdlib/ttimes.nim
+++ b/tests/stdlib/ttimes.nim
@@ -28,6 +28,12 @@ t.checkFormat("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" &
 
 t.checkFormat("yyyyMMddhhmmss", "20380119031407")
 
+# issue 7620
+let t7620_am = parse("4/15/2017 12:01:02 AM +0", "M/d/yyyy' 'h:mm:ss' 'tt' 'z", utc())
+t7620_am.checkFormat("M/d/yyyy' 'h:mm:ss' 'tt' 'z", "4/15/2017 12:01:02 AM +0")
+let t7620_pm = parse("4/15/2017 12:01:02 PM +0", "M/d/yyyy' 'h:mm:ss' 'tt' 'z", utc())
+t7620_pm.checkFormat("M/d/yyyy' 'h:mm:ss' 'tt' 'z", "4/15/2017 12:01:02 PM +0")
+
 let t2 = fromUnix(160070789).utc # Mon 27 Jan 16:06:29 GMT 1975
 t2.checkFormat("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" &
   " ss t tt y yy yyy yyyy yyyyy z zz zzz",
@@ -367,6 +373,10 @@ suite "ttimes":
     check d(seconds = 0) - d(milliseconds = 1500) == d(milliseconds = -1500)
     check d(milliseconds = -1500) == d(seconds = -1, milliseconds = -500)
     check d(seconds = -1, milliseconds = 500) == d(milliseconds = -500)
+    check initDuration(seconds = 1, nanoseconds = 2) <=
+      initDuration(seconds = 1, nanoseconds = 3)
+    check (initDuration(seconds = 1, nanoseconds = 3) <=
+      initDuration(seconds = 1, nanoseconds = 1)).not
 
   test "large/small dates":
     discard initDateTime(1, mJan, -35_000, 12, 00, 00, utc())
@@ -409,4 +419,11 @@ suite "ttimes":
     # Bug with adding a day to a Time
     let day = 24.hours
     let tomorrow = now + day
-    check tomorrow - now == initDuration(days = 1)
\ No newline at end of file
+    check tomorrow - now == initDuration(days = 1)
+  
+  test "fromWinTime/toWinTime":
+    check 0.fromUnix.toWinTime.fromWinTime.toUnix == 0
+    check (-1).fromWinTime.nanosecond == convert(Seconds, Nanoseconds, 1) - 100
+    check -1.fromWinTime.toWinTime == -1
+    # One nanosecond is discarded due to differences in time resolution
+    check initTime(0, 101).toWinTime.fromWinTime.nanosecond == 100 
\ No newline at end of file
diff --git a/tests/stdlib/twchartoutf8.nim b/tests/stdlib/twchartoutf8.nim
index b2f68ee32..a6602e3e3 100644
--- a/tests/stdlib/twchartoutf8.nim
+++ b/tests/stdlib/twchartoutf8.nim
@@ -30,7 +30,6 @@ else:
     result = newString(size)
     let res = WideCharToMultiByte(CP_UTF8, 0'i32, cast[LPWCSTR](addr(wc[0])), wclen,
       cstring(result), size, cstring(nil), LPBOOL(nil))
-    result[size] = chr(0)
     doAssert size == res
 
   proc testCP(wc: WideCString, lo, hi: int) =
diff --git a/tests/system/tnilconcats.nim b/tests/system/tnilconcats.nim
new file mode 100644
index 000000000..ce059b7b0
--- /dev/null
+++ b/tests/system/tnilconcats.nim
@@ -0,0 +1,25 @@
+discard """
+  output: '''@[nil, nil, nil, nil, nil, nil, nil, "meh"]'''
+  exitcode: "0"
+"""
+
+when true:
+  var ab: string
+  ab &= "more"
+
+  doAssert ab == "more"
+
+  var x: seq[string]
+
+  setLen(x, 7)
+
+  x.add "meh"
+
+  var s: string
+  var z = "abc"
+  var zz: string
+  s &= "foo" & z & zz
+
+  doAssert s == "fooabc"
+
+  echo x
diff --git a/tests/system/toString.nim b/tests/system/toString.nim
index ea9d6b05b..37c678a74 100644
--- a/tests/system/toString.nim
+++ b/tests/system/toString.nim
@@ -51,3 +51,59 @@ import strutils
 let arr = ['H','e','l','l','o',' ','W','o','r','l','d','!','\0']
 doAssert $arr == "['H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!', '\\x00']"
 doAssert $cstring(unsafeAddr arr) == "Hello World!"
+
+proc takes(c: cstring) =
+  doAssert c == ""
+
+proc testm() =
+  var x: string
+  # nil is mapped to "":
+  takes(x)
+
+testm()
+
+# nil tests
+var xx: seq[string]
+var yy: string
+doAssert xx == @[]
+doAssert yy == ""
+
+proc bar(arg: cstring): void =
+  doAssert arg[0] == '\0'
+
+proc baz(arg: openarray[char]): void =
+  doAssert arg.len == 0
+
+proc stringCompare(): void =
+  var a,b,c,d,e,f,g: string
+  a.add 'a'
+  doAssert a == "a"
+  b.add "bee"
+  doAssert b == "bee"
+  b.add g
+  doAssert b == "bee"
+  c.add 123.456
+  doAssert c == "123.456"
+  d.add 123456
+  doAssert d == "123456"
+
+  doAssert e == ""
+  doAssert "" == e
+  doAssert nil == e
+  doAssert e == nil
+  doAssert f == g
+  doAssert "" == ""
+  doAssert "" == nil
+  doAssert nil == ""
+
+  g.setLen(10)
+  doAssert g == "\0\0\0\0\0\0\0\0\0\0"
+  doAssert "" != "\0\0\0\0\0\0\0\0\0\0"
+
+  var nilstring: string
+  bar(nilstring)
+  baz(nilstring)
+
+stringCompare()
+static:
+  stringCompare()
\ No newline at end of file
diff --git a/tests/template/i2416.nim b/tests/template/i2416.nim
new file mode 100644
index 000000000..4b53cd0ca
--- /dev/null
+++ b/tests/template/i2416.nim
@@ -0,0 +1 @@
+template i2416*() = echo "i2416"
diff --git a/tests/template/t2416.nim b/tests/template/t2416.nim
new file mode 100644
index 000000000..f73880718
--- /dev/null
+++ b/tests/template/t2416.nim
@@ -0,0 +1,2 @@
+import i2416
+i2416()
diff --git a/tests/testament/backend.nim b/tests/testament/backend.nim
index 4acef9ca4..385f1171c 100644
--- a/tests/testament/backend.nim
+++ b/tests/testament/backend.nim
@@ -13,7 +13,7 @@ type
   CommitId = distinct string
 
 proc `$`*(id: MachineId): string {.borrow.}
-proc `$`(id: CommitId): string {.borrow.}
+#proc `$`(id: CommitId): string {.borrow.} # not used
 
 var
   thisMachine: MachineId
diff --git a/tests/testament/categories.nim b/tests/testament/categories.nim
index 2f9198759..84e536636 100644
--- a/tests/testament/categories.nim
+++ b/tests/testament/categories.nim
@@ -63,6 +63,26 @@ proc compileRodFiles(r: var TResults, cat: Category, options: string) =
   test "gtkex1", true
   test "gtkex2"
 
+# --------------------- flags tests -------------------------------------------
+
+proc flagTests(r: var TResults, cat: Category, options: string) =
+  # --genscript
+  const filename = "tests"/"flags"/"tgenscript"
+  const genopts = " --genscript"
+  let nimcache = nimcacheDir(filename, genopts, targetC)
+  testSpec r, makeTest(filename, genopts, cat)
+
+  when defined(windows):
+    testExec r, makeTest(filename, " cmd /c cd " & nimcache &
+                         " && compile_tgenscript.bat", cat)
+
+  when defined(linux):
+    testExec r, makeTest(filename, " sh -c \"cd " & nimcache &
+                         " && sh compile_tgenscript.sh\"", cat)
+
+  # Run
+  testExec r, makeTest(filename, " " & nimcache / "tgenscript", cat)
+
 # --------------------- DLL generation tests ----------------------------------
 
 proc safeCopyFile(src, dest: string) =
@@ -323,7 +343,7 @@ var nimbleDir = getEnv("NIMBLE_DIR").string
 if nimbleDir.len == 0: nimbleDir = getHomeDir() / ".nimble"
 let
   nimbleExe = findExe("nimble")
-  packageDir = nimbleDir / "pkgs"
+  #packageDir = nimbleDir / "pkgs" # not used
   packageIndex = nimbleDir / "packages.json"
 
 proc waitForExitEx(p: Process): int =
@@ -407,9 +427,9 @@ proc `&.?`(a, b: string): string =
   # candidate for the stdlib?
   result = if b.startswith(a): b else: a & b
 
-proc `&?.`(a, b: string): string =
+#proc `&?.`(a, b: string): string = # not used
   # candidate for the stdlib?
-  result = if a.endswith(b): a else: a & b
+  #result = if a.endswith(b): a else: a & b
 
 proc processSingleTest(r: var TResults, cat: Category, options, test: string) =
   let test = "tests" & DirSep &.? cat.string / test
@@ -429,6 +449,8 @@ proc processCategory(r: var TResults, cat: Category, options: string) =
     jsTests(r, cat, options)
   of "dll":
     dllTests(r, cat, options)
+  of "flags":
+    flagTests(r, cat, options)
   of "gc":
     gcTests(r, cat, options)
   of "longgc":
diff --git a/tests/testament/htmlgen.nim b/tests/testament/htmlgen.nim
index bf26a956d..4a888427e 100644
--- a/tests/testament/htmlgen.nim
+++ b/tests/testament/htmlgen.nim
@@ -121,7 +121,7 @@ proc generateAllTestsContent(outfile: File, allResults: AllTests,
 
 proc generateHtml*(filename: string, onlyFailing: bool) =
   let
-    currentTime = getTime().getLocalTime()
+    currentTime = getTime().local()
     timestring = htmlQuote format(currentTime, "yyyy-MM-dd HH:mm:ss 'UTC'zzz")
   var outfile = open(filename, fmWrite)
 
diff --git a/tests/testament/tester.nim b/tests/testament/tester.nim
index 856f95f3b..0c6f376d3 100644
--- a/tests/testament/tester.nim
+++ b/tests/testament/tester.nim
@@ -16,7 +16,7 @@ import
 
 const
   resultsFile = "testresults.html"
-  jsonFile = "testresults.json"
+  #jsonFile = "testresults.json" # not used
   Usage = """Usage:
   tester [options] command [arguments]
 
@@ -150,11 +150,11 @@ proc initResults: TResults =
   result.skipped = 0
   result.data = ""
 
-proc readResults(filename: string): TResults =
-  result = marshal.to[TResults](readFile(filename).string)
+#proc readResults(filename: string): TResults = # not used
+#  result = marshal.to[TResults](readFile(filename).string)
 
-proc writeResults(filename: string, r: TResults) =
-  writeFile(filename, $$r)
+#proc writeResults(filename: string, r: TResults) = # not used
+#  writeFile(filename, $$r)
 
 proc `$`(x: TResults): string =
   result = ("Tests passed: $1 / $3 <br />\n" &
@@ -212,18 +212,18 @@ proc cmpMsgs(r: var TResults, expected, given: TSpec, test: TTest, target: TTarg
   elif expected.tfile == "" and extractFilename(expected.file) != extractFilename(given.file) and
       "internal error:" notin expected.msg:
     r.addResult(test, target, expected.file, given.file, reFilesDiffer)
-  elif expected.line   != given.line   and expected.line   != 0 or
+  elif expected.line != given.line and expected.line != 0 or
        expected.column != given.column and expected.column != 0:
     r.addResult(test, target, $expected.line & ':' & $expected.column,
-                      $given.line    & ':' & $given.column,
+                      $given.line & ':' & $given.column,
                       reLinesDiffer)
   elif expected.tfile != "" and extractFilename(expected.tfile) != extractFilename(given.tfile) and
       "internal error:" notin expected.msg:
     r.addResult(test, target, expected.tfile, given.tfile, reFilesDiffer)
-  elif expected.tline   != given.tline   and expected.tline   != 0 or
+  elif expected.tline != given.tline and expected.tline != 0 or
        expected.tcolumn != given.tcolumn and expected.tcolumn != 0:
     r.addResult(test, target, $expected.tline & ':' & $expected.tcolumn,
-                      $given.tline    & ':' & $given.tcolumn,
+                      $given.tline & ':' & $given.tcolumn,
                       reLinesDiffer)
   else:
     r.addResult(test, target, expected.msg, given.msg, reSuccess)
@@ -404,6 +404,21 @@ proc testC(r: var TResults, test: TTest) =
     if exitCode != 0: given.err = reExitCodesDiffer
   if given.err == reSuccess: inc(r.passed)
 
+proc testExec(r: var TResults, test: TTest) =
+  # runs executable or script, just goes by exit code
+  inc(r.total)
+  let (outp, errC) = execCmdEx(test.options.strip())
+  var given: TSpec
+  specDefaults(given)
+  if errC == 0:
+    given.err = reSuccess
+  else:
+    given.err = reExitCodesDiffer
+    given.msg = outp.string
+
+  if given.err == reSuccess: inc(r.passed)
+  r.addResult(test, targetC, "", given.msg, given.err)
+
 proc makeTest(test, options: string, cat: Category, action = actionCompile,
               env: string = ""): TTest =
   # start with 'actionCompile', will be overwritten in the spec:
diff --git a/tests/threads/tthreadvars.nim b/tests/threads/tthreadvars.nim
new file mode 100644
index 000000000..81aa2e5ec
--- /dev/null
+++ b/tests/threads/tthreadvars.nim
@@ -0,0 +1,78 @@
+discard """
+output: '''
+10
+1111
+1222
+3030303
+3060606
+6060606
+6121212
+3030903
+3061206
+3031503
+3061806
+5050505
+5101010
+'''
+"""
+
+import typetraits
+
+var tls1 {.threadvar.}: int
+var g0: int
+var g1 {.global.}: int
+
+proc customInc(x: var int, delta: int) =
+  x += delta
+
+customInc(tls1, 10)
+echo tls1
+
+proc nonGenericProc: int =
+  var local: int
+  var nonGenericTls {.threadvar.}: int
+  var nonGenericGlobal {.global.}: int
+  var nonGenericMixedPragmas {.global, threadvar.}: int
+
+  customInc local, 1000
+  customInc nonGenericTls, 1
+  customInc nonGenericGlobal, 10
+  customInc nonGenericMixedPragmas, 100
+
+  return local + nonGenericTls + nonGenericGlobal + nonGenericMixedPragmas
+
+proc genericProc(T: typedesc): int =
+  var local: int
+  var genericTls {.threadvar.}: int
+  var genericGlobal {.global.}: int
+  var genericMixedPragmas {.global, threadvar.}: int
+
+  customInc local, T.name.len * 1000000
+  customInc genericTls, T.name.len * 1
+  customInc genericGlobal, T.name.len * 100
+  customInc genericMixedPragmas, T.name.len * 10000
+
+  return local + genericTls + genericGlobal + genericMixedPragmas
+
+echo nonGenericProc()
+echo nonGenericProc()
+
+echo genericProc(int)
+echo genericProc(int)
+
+echo genericProc(string)
+echo genericProc(string)
+
+proc echoInThread[T]() {.thread.} =
+  echo genericProc(T)
+  echo genericProc(T)
+
+proc newEchoThread(T: typedesc) =
+  var t: Thread[void]
+  createThread(t, echoInThread[T])
+  joinThreads(t)
+
+newEchoThread int
+newEchoThread int
+newEchoThread float
+
diff --git a/tests/trmacros/thoist.nim b/tests/trmacros/thoist.nim
index 7d14c0abf..657f210a1 100644
--- a/tests/trmacros/thoist.nim
+++ b/tests/trmacros/thoist.nim
@@ -5,7 +5,7 @@ true'''
 
 import pegs
 
-template optPeg{peg(pattern)}(pattern: string{lit}): TPeg =
+template optPeg{peg(pattern)}(pattern: string{lit}): Peg =
   var gl {.global, gensym.} = peg(pattern)
   gl
 
diff --git a/tests/typerel/t7600_1.nim b/tests/typerel/t7600_1.nim
new file mode 100644
index 000000000..e3a5fefa2
--- /dev/null
+++ b/tests/typerel/t7600_1.nim
@@ -0,0 +1,18 @@
+discard """
+errormsg: "type mismatch: got <Thin[system.int]>"
+nimout: '''t7600_1.nim(18, 6) Error: type mismatch: got <Thin[system.int]>
+but expected one of:
+proc test[T](x: Paper[T])
+
+expression: test tn'''
+"""
+
+type
+  Paper[T] = ref object of RootObj
+    thickness: T
+  Thin[T]  = object of Paper[T]
+
+proc test[T](x: Paper[T]) = discard
+
+var tn = Thin[int]()
+test tn
diff --git a/tests/typerel/t7600_2.nim b/tests/typerel/t7600_2.nim
new file mode 100644
index 000000000..7badb69cf
--- /dev/null
+++ b/tests/typerel/t7600_2.nim
@@ -0,0 +1,17 @@
+discard """
+errormsg: "type mismatch: got <Thin>"
+nimout: '''t7600_2.nim(17, 6) Error: type mismatch: got <Thin>
+but expected one of:
+proc test(x: Paper)
+
+expression: test tn'''
+"""
+
+type
+  Paper = ref object of RootObj
+  Thin  = object of Paper
+
+proc test(x: Paper) = discard
+
+var tn = Thin()
+test tn
diff --git a/tests/typerel/texplicitcmp.nim b/tests/typerel/texplicitcmp.nim
index 8aec9885a..e91ac2ffe 100644
--- a/tests/typerel/texplicitcmp.nim
+++ b/tests/typerel/texplicitcmp.nim
@@ -18,7 +18,7 @@ proc works() =
   sort(f, system.cmp[int])
   outp(f)
 
-proc weird(json_params: TTable) =
+proc weird(json_params: Table) =
   var f = @[3, 2, 1]
   # The following line doesn't compile: type mismatch. Why?
   sort(f, system.cmp[int])
@@ -29,4 +29,4 @@ when isMainModule:
   sort(t, system.cmp[int])
   outp(t)
   works()
-  weird(initTable[string, TJsonNode]())
+  weird(initTable[string, JsonNode]())
diff --git a/tests/typerel/ttypelessemptyset.nim b/tests/typerel/ttypelessemptyset.nim
index 3e171387b..5f49c33fd 100644
--- a/tests/typerel/ttypelessemptyset.nim
+++ b/tests/typerel/ttypelessemptyset.nim
@@ -1,5 +1,5 @@
 discard """
-  errormsg: "internal error: invalid kind for last(tyEmpty)"
+  errormsg: "internal error: invalid kind for lastOrd(tyEmpty)"
 """
 var q = false
 discard (if q: {} else: {})
diff --git a/tests/types/tparameterizedparent3.nim b/tests/types/tparameterizedparent3.nim
index 3fc83cb4d..58aaf80ea 100644
--- a/tests/types/tparameterizedparent3.nim
+++ b/tests/types/tparameterizedparent3.nim
@@ -1,7 +1,7 @@
 discard """
   file: "tparameterizedparent3.nim"
   line: 13
-  errormsg: "redefinition of 'color'"
+  errormsg: "attempt to redefine: 'color'"
 """
 # bug #5264
 type
diff --git a/tests/types/tparameterizedparent4.nim b/tests/types/tparameterizedparent4.nim
index fa8b525c1..a37461bb4 100644
--- a/tests/types/tparameterizedparent4.nim
+++ b/tests/types/tparameterizedparent4.nim
@@ -1,7 +1,7 @@
 discard """
   file: "tparameterizedparent4.nim"
   line: 23
-  errormsg: "redefinition of 'grain'"
+  errormsg: "attempt to redefine: 'grain'"
 """
 # bug #5264
 type
diff --git a/tests/varres/tnewseq_on_result_vart.nim b/tests/varres/tnewseq_on_result_vart.nim
new file mode 100644
index 000000000..18935a1d1
--- /dev/null
+++ b/tests/varres/tnewseq_on_result_vart.nim
@@ -0,0 +1,9 @@
+
+discard """
+  line: 9
+  errormsg: "address of 'result' may not escape its stack frame"
+"""
+# bug #5113
+
+proc makeSeqVar(size: Natural): var seq[int] =
+  newSeq(result, size)
diff --git a/tests/vm/tref.nim b/tests/vm/tref.nim
new file mode 100644
index 000000000..517a67fb0
--- /dev/null
+++ b/tests/vm/tref.nim
@@ -0,0 +1,12 @@
+static:
+  var
+    a: ref string
+    b: ref string
+  new a
+
+  a[] = "Hello world"
+  b = a
+
+  b[5] = 'c'
+  doAssert a[] == "Hellocworld"
+  doAssert b[] == "Hellocworld"
\ No newline at end of file
diff --git a/tools/finish.nim b/tools/finish.nim
index 2681f7ccf..4f2c72595 100644
--- a/tools/finish.nim
+++ b/tools/finish.nim
@@ -52,14 +52,17 @@ when defined(windows):
   proc askBool(m: string): bool =
     stdout.write m
     while true:
-      let answer = stdin.readLine().normalize
-      case answer
-      of "y", "yes":
-        return true
-      of "n", "no":
-        return false
-      else:
-        echo "Please type 'y' or 'n'"
+      try:
+        let answer = stdin.readLine().normalize
+        case answer
+        of "y", "yes":
+          return true
+        of "n", "no":
+          return false
+        else:
+          echo "Please type 'y' or 'n'"
+      except EOFError:
+        quit(1)
 
   proc askNumber(m: string; a, b: int): int =
     stdout.write m
@@ -99,10 +102,6 @@ when defined(windows):
 
   proc addToPathEnv*(e: string) =
     var p = tryGetUnicodeValue(r"Environment", "Path", HKEY_CURRENT_USER)
-    if p.len == 0:
-      p = tryGetUnicodeValue(
-        r"SYSTEM\CurrentControlSet\Control\Session Manager\Environment",
-        "Path", HKEY_LOCAL_MACHINE)
     let x = if e.contains(Whitespace): "\"" & e & "\"" else: e
     if p.len > 0:
       p.add ";"
@@ -188,10 +187,14 @@ when defined(windows):
 
 proc main() =
   when defined(windows):
-    let desiredPath = expandFilename(getCurrentDir() / "bin")
-    let p = getUnicodeValue(r"Environment", "Path",
-      HKEY_CURRENT_USER)
-    var alreadyInPath = false
+    let nimDesiredPath = expandFilename(getCurrentDir() / "bin")
+    let nimbleDesiredPath = expandFilename(getEnv("USERPROFILE") / ".nimble" / "bin")
+    let p = tryGetUnicodeValue(r"Environment", "Path",
+      HKEY_CURRENT_USER) & ";" & tryGetUnicodeValue(
+        r"System\CurrentControlSet\Control\Session Manager\Environment", "Path",
+        HKEY_LOCAL_MACHINE)
+    var nimAlreadyInPath = false
+    var nimbleAlreadyInPath = false
     var mingWchoices: seq[string] = @[]
     var incompat: seq[string] = @[]
     for x in p.split(';'):
@@ -199,18 +202,29 @@ proc main() =
       let y = try: expandFilename(if x[0] == '"' and x[^1] == '"':
                                     substr(x, 1, x.len-2) else: x)
               except: ""
-      if y == desiredPath: alreadyInPath = true
-      if y.toLowerAscii.contains("mingw"):
+      if y.cmpIgnoreCase(nimDesiredPath) == 0:
+        nimAlreadyInPath = true
+      elif y.cmpIgnoreCase(nimbleDesiredPath) == 0:
+        nimbleAlreadyInPath = true
+      elif y.toLowerAscii.contains("mingw"):
         if dirExists(y):
           if checkGccArch(y): mingWchoices.add y
           else: incompat.add y
 
-    if alreadyInPath:
-      echo "bin/nim.exe is already in your PATH [Skipping]"
+    if nimAlreadyInPath:
+      echo "bin\\nim.exe is already in your PATH [Skipping]"
     else:
       if askBool("nim.exe is not in your PATH environment variable.\n" &
           "Should it be added permanently? (y/n) "):
-        addToPathEnv(desiredPath)
+        addToPathEnv(nimDesiredPath)
+
+    if nimbleAlreadyInPath:
+      echo nimbleDesiredPath & " is already in your PATH [Skipping]"
+    else:
+      if askBool(nimbleDesiredPath & " is not in your PATH environment variable.\n" &
+          "Should it be added permanently? (y/n) "):
+        addToPathEnv(nimbleDesiredPath)
+
     if mingWchoices.len == 0:
       # No mingw in path, so try a few locations:
       let alternative = tryDirs(incompat, defaultMingwLocations())
diff --git a/tools/nim.bash-completion b/tools/nim.bash-completion
index 1c199a0cf..8e569079a 100644
--- a/tools/nim.bash-completion
+++ b/tools/nim.bash-completion
@@ -15,7 +15,7 @@ _nim()
    return 0
  fi
   case $prev in
-    --stackTrace|--lineTrace|--threads|-x|--checks|--objChecks|--fieldChecks|--rangeChecks|--boundChecks|--overflowChecks|-a|--assertions|--floatChecks|--nanChecks|--infChecks|--deadCodeElim)
+    --stackTrace|--lineTrace|--threads|-x|--checks|--objChecks|--fieldChecks|--rangeChecks|--boundChecks|--overflowChecks|-a|--assertions|--floatChecks|--nanChecks|--infChecks)
       # Options that require on/off
       [[ "$cur" == "=" ]] && cur=""
       COMPREPLY=( $(compgen -W 'on off' -- "$cur") )
@@ -32,7 +32,7 @@ _nim()
       return 0
     ;;
     *)
-      kw="-r -p= --path= -d= --define= -u= --undef= -f --forceBuild --opt= --app= --stackTrace= --lineTrace= --threads= -x= --checks= --objChecks= --fieldChecks= --rangeChecks= --boundChecks= --overflowChecks= -a= --assertions= --floatChecks= --nanChecks= --infChecks= --deadCodeElim="
+      kw="-r -p= --path= -d= --define= -u= --undef= -f --forceBuild --opt= --app= --stackTrace= --lineTrace= --threads= -x= --checks= --objChecks= --fieldChecks= --rangeChecks= --boundChecks= --overflowChecks= -a= --assertions= --floatChecks= --nanChecks= --infChecks="
       COMPREPLY=( $( compgen -W "${kw}" -- $cur ) )
       _filedir '@(nim)'
       #$split
diff --git a/tools/nim.zsh-completion b/tools/nim.zsh-completion
index 57b5e163e..e9b9002fb 100644
--- a/tools/nim.zsh-completion
+++ b/tools/nim.zsh-completion
@@ -60,8 +60,6 @@ _nim() {
     '*--infChecks=off[turn Inf checks off]' \
     '*--nilChecks=on[turn nil checks on]' \
     '*--nilChecks=off[turn nil checks off]' \
-    '*--deadCodeElim=on[whole program dead code elimination on]' \
-    '*--deadCodeElim=off[whole program dead code elimination off]' \
     '*--opt=none[do not optimize]' \
     '*--opt=speed[optimize for speed|size - use -d:release for a release build]' \
     '*--opt=size[optimize for size]' \
diff --git a/tools/nimgrep.nim b/tools/nimgrep.nim
index 8dc076ad9..9cfd7a86f 100644
--- a/tools/nimgrep.nim
+++ b/tools/nimgrep.nim
@@ -11,7 +11,7 @@ import
   os, strutils, parseopt, pegs, re, terminal
 
 const
-  Version = "1.1"
+  Version = "1.2"
   Usage = "nimgrep - Nim Grep Utility Version " & Version & """
 
   (c) 2012 Andreas Rumpf
@@ -35,6 +35,8 @@ Options:
   --nocolor           output will be given without any colours.
   --oneline           show file on each matched line
   --verbose           be verbose: list every processed file
+  --filenames         find the pattern in the filenames, not in the contents
+                      of the file
   --help, -h          shows this help
   --version, -v       shows the version
 """
@@ -42,7 +44,7 @@ Options:
 type
   TOption = enum
     optFind, optReplace, optPeg, optRegex, optRecursive, optConfirm, optStdin,
-    optWord, optIgnoreCase, optIgnoreStyle, optVerbose
+    optWord, optIgnoreCase, optIgnoreStyle, optVerbose, optFilenames
   TOptions = set[TOption]
   TConfirmEnum = enum
     ceAbort, ceYes, ceAll, ceNo, ceNone
@@ -126,7 +128,7 @@ proc highlight(s, match, repl: string, t: tuple[first, last: int],
     stdout.write("\n")
     stdout.flushFile()
 
-proc processFile(pattern; filename: string) =
+proc processFile(pattern; filename: string; counter: var int) =
   var filenameShown = false
   template beforeHighlight =
     if not filenameShown and optVerbose notin options and not oneline:
@@ -135,11 +137,14 @@ proc processFile(pattern; filename: string) =
       filenameShown = true
 
   var buffer: string
-  try:
-    buffer = system.readFile(filename)
-  except IOError:
-    echo "cannot open file: ", filename
-    return
+  if optFilenames in options:
+    buffer = filename
+  else:
+    try:
+      buffer = system.readFile(filename)
+    except IOError:
+      echo "cannot open file: ", filename
+      return
   if optVerbose in options:
     stdout.writeLine(filename)
     stdout.flushFile()
@@ -161,6 +166,7 @@ proc processFile(pattern; filename: string) =
     var wholeMatch = buffer.substr(t.first, t.last)
 
     beforeHighlight()
+    inc counter
     if optReplace notin options:
       highlight(buffer, wholeMatch, "", t, filename, line, showRepl=false)
     else:
@@ -236,17 +242,17 @@ proc styleInsensitive(s: string): string =
         addx()
     else: addx()
 
-proc walker(pattern; dir: string) =
+proc walker(pattern; dir: string; counter: var int) =
   for kind, path in walkDir(dir):
     case kind
     of pcFile:
       if extensions.len == 0 or path.hasRightExt(extensions):
-        processFile(pattern, path)
+        processFile(pattern, path, counter)
     of pcDir:
       if optRecursive in options:
-        walker(pattern, path)
+        walker(pattern, path, counter)
     else: discard
-  if existsFile(dir): processFile(pattern, dir)
+  if existsFile(dir): processFile(pattern, dir, counter)
 
 proc writeHelp() =
   stdout.write(Usage)
@@ -293,6 +299,7 @@ for kind, key, val in getopt():
     of "nocolor": useWriteStyled = false
     of "oneline": oneline = true
     of "verbose": incl(options, optVerbose)
+    of "filenames": incl(options, optFilenames)
     of "help", "h": writeHelp()
     of "version", "v": writeVersion()
     else: writeHelp()
@@ -304,6 +311,7 @@ when defined(posix):
 checkOptions({optFind, optReplace}, "find", "replace")
 checkOptions({optPeg, optRegex}, "peg", "re")
 checkOptions({optIgnoreCase, optIgnoreStyle}, "ignore_case", "ignore_style")
+checkOptions({optFilenames, optReplace}, "filenames", "replace")
 
 if optStdin in options:
   pattern = ask("pattern [ENTER to exit]: ")
@@ -314,6 +322,7 @@ if optStdin in options:
 if pattern.len == 0:
   writeHelp()
 else:
+  var counter = 0
   if filenames.len == 0:
     filenames.add(os.getCurrentDir())
   if optRegex notin options:
@@ -325,7 +334,7 @@ else:
       pattern = "\\i " & pattern
     let pegp = peg(pattern)
     for f in items(filenames):
-      walker(pegp, f)
+      walker(pegp, f, counter)
   else:
     var reflags = {reStudy, reExtended}
     if optIgnoreStyle in options:
@@ -336,5 +345,6 @@ else:
       reflags.incl reIgnoreCase
     let rep = re(pattern, reflags)
     for f in items(filenames):
-      walker(rep, f)
-
+      walker(rep, f, counter)
+  if not oneline:
+    stdout.write($counter & " matches\n")
diff --git a/tools/niminst/buildsh.tmpl b/tools/niminst/buildsh.tmpl
index 3e7d8ae6e..be6d43754 100644
--- a/tools/niminst/buildsh.tmpl
+++ b/tools/niminst/buildsh.tmpl
@@ -39,8 +39,8 @@ do
   esac
 done
 
-CC="gcc"
-LINKER="gcc"
+CC="${CC:-gcc}"
+LINKER="${LD:-gcc}"
 COMP_FLAGS="${CPPFLAGS:-} ${CFLAGS:-} ?{c.ccompiler.flags}$extraBuildArgs"
 LINK_FLAGS="${LDFLAGS:-} ?{c.linker.flags}"
 PS4=""
diff --git a/tools/nimpretty.nim b/tools/nimpretty.nim
index 36d1382cf..396f17b0b 100644
--- a/tools/nimpretty.nim
+++ b/tools/nimpretty.nim
@@ -24,7 +24,7 @@ const
 Usage:
   nimpretty [options] file.nim
 Options:
-  --backup:ON|OFF     create a backup file before overwritting (default: ON)
+  --backup:on|off     create a backup file before overwritting (default: ON)
   --version           show the version
   --help              show this help
 """
@@ -43,7 +43,7 @@ proc prettyPrint(infile: string) =
   let fileIdx = fileInfoIdx(infile)
   let tree = parseFile(fileIdx, newIdentCache())
   let outfile = changeFileExt(infile, ".pretty.nim")
-  renderModule(tree, infile, outfile, {})
+  renderModule(tree, infile, outfile, {}, fileIdx)
 
 proc main =
   var infile: string
diff --git a/web/website.ini b/web/website.ini
index 56b7c436f..18e7bf2cb 100644
--- a/web/website.ini
+++ b/web/website.ini
@@ -31,7 +31,7 @@ file: ticker.html
 [Documentation]
 doc: "endb.rst;intern.txt;apis.txt;lib.rst;manual.rst;tut1.rst;tut2.rst;nimc.rst;overview.rst;filters.rst"
 doc: "tools.txt;niminst.rst;nimgrep.rst;gc.rst;estp.rst;idetools.rst;docgen.rst;koch.rst;backends.txt"
-doc: "nimfix.rst;nimsuggest.rst;nep1.rst;nims.rst;contributing.rst"
+doc: "nimfix.rst;nimsuggest.rst;nep1.rst;nims.rst;contributing.rst;manual/*.rst"
 pdf: "manual.rst;lib.rst;tut1.rst;tut2.rst;nimc.rst;niminst.rst;gc.rst"
 srcdoc2: "system.nim;system/nimscript;pure/ospaths"
 srcdoc2: "core/macros;pure/marshal;core/typeinfo"